How To Submit Gravity Forms With Javascript

Posted: May 14th, 2020

Topics: Wordpress, React, Gatsby, Javascript, Cloud Functions

I just wrapped up my first project using Wordpress as a headless CMS with React (well, Gatsby). It was pretty nice going back to using Wordpress. ACF really makes the developer experience for creating data models quick and easy. Plus it's nice for a client to see a familiar face in Wordpress.

If you're a Wordpress person, you've probably used Gravity Forms. Probably the best turnkey form solution for WP forms out there. When using other headless CMS solutions, I typically just write a little cloud function to pass along form submissions to the inbox of the point of contact via Sendgrid or Mailgun. But now using WP, I want all the form submissions to go through Gravity Forms.

This was not very fun, but it ended up coming together nicely as a cloud function which takes a payload of form fields, generates a secure endpoint using Gravity Forms V1 endpoint, hits the GF api on your site, then returns what comes back to the client.

Disclaimer, there appear to be some prepackaged solutions for handling Gravity Forms in React, but where's the fun in that. Here's what my cloud function looks like (this was written as a Netlify function, but could easily be adapted as a traditional endpoint or other cloud provider function or even gasp jQuery - you definitely shouldn't expose it to the client, however):

import CryptoJS from 'crypto-js';
import rp from 'request-promise';

// This is a function slightly amended from the 
// Gravity Forms V1 API documentation.  It generates 
// a signature to append to your endpoint as a security 
// measure.  Kinda extra but kinda cool.

function calculateSignature(stringToSign, privateKey) {
  const hash = CryptoJS.HmacSHA1(stringToSign, privateKey);
  const base64 = hash.toString(CryptoJS.enc.Base64);
  return encodeURIComponent(base64);
}
// These are a bunch of variables for building
// the api endpoint. 

const d = new Date(),
  expiration = 60,
  unixtime = parseInt(d.getTime() / 1000),
  future_unixtime = unixtime + expiration,
  publicKey = MY_PUBLIC_API_KEY,
  privateKey = MY_PRIVATE_API_KEY,
  method = 'POST',
  formId = MY_FORM_ID,
  route = `forms/${formId}/submissions`,
  stringToSign = publicKey + ':' + method + ':' + route + ':' + future_unixtime,
  sig = calculateSignature(stringToSign, privateKey),
	
	// I have an HT Access lock on my WP backend, so if yours is 
	// public, you don't need this auth.  Otherwise add those creds here
	// as a basic auth header
	
  auth =
    'Basic ' +
    new Buffer(HT_ACCESS_USER + ':' + HT_ACCESS_PASS).toString('base64');

const uri =
  'https://www.MY_DOMAIN_NAME.com/gravityformsapi/' +
  route +
  '?api_key=' +
  publicKey +
  '&signature=' +
  sig +
  '&expires=' +
  future_unixtime;

const headers = {
  'Access-Control-Allow-Origin': '*',
  'Access-Control-Allow-Headers': 'Content-Type',
};

exports.handler = function(event, context, callback) {
  let data = JSON.parse(event.body);
  let { name, email, subject, message } = data;

	// This is the data shape that the API expects. I believe the 
	// integer value on the keys map to a specific value on the 
	// fields in your gravity form, so if you delete one somewhere 
	// down the line, it could look like input_1, input_3, input_4, etc.
	
  const values = {
    input_values: {
      input_1: name,
      input_2: email,
      input_3: subject,
      input_4: message,
    },
  };

  rp({
    uri,
    method: 'POST',
    body: values,
    headers: {
			// Again, auth to get past my HT Access lock
      Authorization: auth,
    },
    json: true,
  })
    .then(res => {
      console.log(res);
      callback(null, {
        statusCode: 200,
        headers,
        body: JSON.stringify(res),
      });
    })
    .catch(err => {
      console.log(err);
      callback(null, {
        statusCode: 401,
        headers,
        body: JSON.stringify(err),
      });
    });
};

Hope this helps! Holler at me if you need a web developer.