Fetching data from Formik form using Gatsby and use Netlify functions to post into Sanity

Hi,

I’m new using Netlify functions, I’m using Gatsby with Sanity on Netlfy. I’m trying to use function for user to post new job into sanity using Netlify function. I’ve started setting up a test form but I’m struggling to get everything working together.

Here my form using formik with yup:

const jobPostSchema = Yup.object().shape({
  title: Yup.string()
    .min(2, 'Too Short!')
    .max(50, 'Too Long!')
    .required('Job title is required.'),
  email: Yup.string()
    .email('Invalid email address')
    .required('Email is required!'),
})

const encode = data => {
  return Object.keys(data)
    .map(key => encodeURIComponent(key) + '=' + encodeURIComponent(data[key]))
    .join('&')
}

export default class NewJob extends React.Component {
  render() {
    return (
      <Layout>
        <ContentWrapFull>
          <PageHeading>Post a job</PageHeading>

          <Formik
            initialValues={{ title: '', email: '' }}
            validationSchema={jobPostSchema}
            onSubmit={(payload, { setSubmitting }) => {
              fetch('/?no-cache=1', {
                method: 'POST',
                headers: {
                  'Content-Type': 'application/x-www-form-urlencoded',
                },
                body: encode({ 'form-name': 'jobsubmit', payload }),
              })
              alert(payload.title)
              console.log(payload)
              setSubmitting(false)
            }}
          >
            {({
              values,
              errors,
              dirty,
              touched,
              handleChange,
              handleBlur,
              handleSubmit,
              handleReset,
              isSubmitting,
              /* and other goodies */
            }) => (
              <form
                name="jobsubmit"
                method="post"
                action="/thanks/"
                data-netlify="true"
                onSubmit={handleSubmit}
              >
                <p>
                  <FormLabel>
                    Job position*
                    <br />
                    <FormInput
                      type="text"
                      name="title"
                      onChange={handleChange}
                      onBlur={handleBlur}
                      value={values.title}
                    />
                    {errors.title && touched.title && errors.title}
                  </FormLabel>
                </p>
                <p>
                  <FormLabel>
                    Application Email or URL
                    <br />
                    <FormInput
                      type="email"
                      name="email"
                      onChange={handleChange}
                      onBlur={handleBlur}
                      value={values.email}
                    />
                    {errors.email && touched.email && errors.email}
                  </FormLabel>
                </p>

                <button
                  type="button"
                  onClick={handleReset}
                  disabled={!dirty || isSubmitting}
                >
                  Reset
                </button>
                <button type="submit" disabled={isSubmitting}>
                  Submit
                </button>
              </form>
            )}
          </Formik>
        </ContentWrapFull>
      </Layout>
    )
  }
}

Here my function:

const sanityClient = require('@sanity/client')
require('dotenv').config({
  path: `.env.${process.env.NODE_ENV}`,
})
const client = sanityClient({
  projectId: 'idnumber',
  dataset: 'databasename',
  token: process.env.SANITY_TOKEN_WRITE,
})

exports.handler = async function(event, context, callback) {

  let payload = JSON.stringify(event.body).payload

  let result = await client.create({ _type: 'job', ...payload })

  callback(null, {
    statusCode: 200,
    body: JSON.stringify(result),
  })

  console.log(data)
}

Any suggestion would be welcome, thank you.

Hi Jerome,

Better to back up a few steps and make sure all the basic pieces work before trying to debug :slight_smile:

First off, does your form work in Netlify? You get a 200 when POST’ing, the data from the POST is saved and shown in your netlify dashboard? If so, great start! If not, check out our forms debugging articles such as [Common Issue] How to debug your form as well as "Page Not Found" when submitting form and finally since you use gatsby, also see this blog post about gatsby+netlify that touches on form setup

If that does already work, awesome! That’s probably the biggest hurdle. Next up, how are you attempting to process the POST? Perhaps using an event triggered function? If so, this article may point the way: Debugging event-triggered functions

Let us know more about what you’re doing and how you’re trying to do it and we’ll probably be able to give some more specific guidance, short of debugging your code (which is only half of the picture :))

3 suggestions:

First, inside of your function you no longer need to use something like dotenv package. You can just do something like this:

const sanityClient = require('@sanity/client')
const { 
  SANITY_TOKEN_WRITE
} = process.env

const client = sanityClient({
  projectId: 'idnumber',
  dataset: 'databasename',
  token: SANITY_TOKEN_WRITE,
})

Make sure you set your SANITY_TOKEN_WRITE environment variable in the site settings.

And second, with async functions you no longer need the callback in your function, so you can do something like this:

exports.handler = async function(event, context, callback) {

  let payload = JSON.stringify(event.body).payload

  let result = await client.create({ _type: 'job', ...payload })
  console.log('result', result)
  return {
    statusCode: 200,
    body: JSON.stringify(result),
  }

}

And third, you can test out these functions locally with the new netlify dev command.

npm install netlify-cli -g

# then run the functions locally

netlify dev

More info on the dev command can be found here https://scotch.io/tutorials/netlify-dev-the-power-of-netlify-on-your-local-computer

2 Likes

Thank you so much David making some progress, When I’m checking the functions using Netlify dev getting some data but not the payload data from my form, any suggestions?

Not sure about that.

What happens when you deploy the live function to netlify?

1 Like

Here the function log after being deployed

    4:52:25 PM: submission-created invoked
    4:52:26 PM: Unable to import module 'submission-created': Error    at Function.Module._resolveFilename (module.js:547:15)    at Function.Module._load (module.js:474:25)    at Module.require (module.js:596:17)    at require (internal/module.js:11:18)    at Object.<anonymous> (/var/task/submission-created.js:1:84)    at Module._compile (module.js:652:30)    at Object.Module._extensions..js (module.js:663:10)    at Module.load (module.js:565:32)    at tryModuleLoad (module.js:505:12)    at Function.Module._load (module.js:497:3)
    4:52:53 PM: submission-created invoked
    4:52:53 PM: Unable to import module 'submission-created': Error    at Function.Module._load (module.js:474:25)    at Module.require (module.js:596:17)    at require (internal/module.js:11:18)    at Object.<anonymous> (/var/task/submission-created.js:1:84)    at Module._compile (module.js:652:30)    at Object.Module._extensions..js (module.js:663:10)    at Module.load (module.js:565:32)    at tryModuleLoad (module.js:505:12)    at Function.Module._load (module.js:497:3)

Sounds like the module is not found.

Is there a package.json file inside your functions folder with the @sanity/client package (and any other dep used in functions)

Ok I’ve updated my code getting the correct object data via the form,but still can get the function to push the form data into Sanity. I’m not seeing any error.

Here the code from my post-a-job.js file

import React from 'react'
import Layout from '../components/layout'
import { navigate } from 'gatsby-link'
import ContentWrapFull from '../components/content-wrap-full'
import PageHeading from '../components/heading'
import FormInput from '../components/form/input'
import FormLabel from '../components/form/label'
import FormSelect from '../components/form/select'
import FormTextArea from '../components/form/textarea'
import { Formik } from 'formik'
import * as Yup from 'yup'

const jobPostSchema = Yup.object().shape({
  title: Yup.string()
    .min(2, 'Too Short!')
    .max(50, 'Too Long!')
    .required('Job title is required.'),
  email: Yup.string()
    .email('Invalid email address')
    .required('Email is required!'),
})

const encode = data =>
  Object.keys(data)
    .map(key => encodeURIComponent(key) + '=' + encodeURIComponent(data[key]))
    .join('&')

export default class NewJob extends React.Component {
  render() {
    return (
      <Layout>
        <ContentWrapFull>
          <PageHeading>Post a job</PageHeading>

          <Formik
            initialValues={{ title: '', email: '' }}
            validationSchema={jobPostSchema}
            onSubmit={(payload, { setSubmitting }) => {
              console.log({ payload })
              const body = encode({ 'form-name': 'jobsubmit', ...payload })

              console.log(body)
              fetch('/?no-cache=1', {
                method: 'POST',
                headers: {
                  'Content-Type': 'application/x-www-form-urlencoded',
                },
                body,
              })
              alert(payload.title)
              console.log(payload)
              setSubmitting(false)
            }}
          >
            {({
              values,
              errors,
              dirty,
              touched,
              handleChange,
              handleBlur,
              handleSubmit,
              handleReset,
              isSubmitting,
              /* and other goodies */
            }) => (
              <form
                name="jobsubmit"
                method="post"
                action="/thanks/"
                data-netlify="true"
                onSubmit={handleSubmit}
              >
                <p>
                  <FormLabel>
                    Job position*
                    <br />
                    <FormInput
                      type="text"
                      name="title"
                      onChange={handleChange}
                      onBlur={handleBlur}
                      value={values.title}
                    />
                    {errors.title && touched.title && errors.title}
                  </FormLabel>
                </p>
                <p>
                  <FormLabel>
                    Application Email or URL
                    <br />
                    <FormInput
                      type="email"
                      name="email"
                      onChange={handleChange}
                      onBlur={handleBlur}
                      value={values.email}
                    />
                    {errors.email && touched.email && errors.email}
                  </FormLabel>
                </p>

                <button
                  type="button"
                  onClick={handleReset}
                  disabled={!dirty || isSubmitting}
                >
                  Reset
                </button>
                <button type="submit" disabled={isSubmitting}>
                  Submit
                </button>
              </form>
            )}
          </Formik>
        </ContentWrapFull>
      </Layout>
    )
  }
}

Here my functions file submission-created.js (not seeing the …payload data)

const sanityClient = require('@sanity/client')
require('dotenv').config({
  path: `.env.${process.env.NODE_ENV}`,
})
const { SANITY_TOKEN_WRITE } = process.env
const client = sanityClient({
  projectId: 'someID',
  dataset: 'someDataset',
  token: SANITY_TOKEN_WRITE,
})

exports.handler = async function(event, context, callback) {
  let payload = JSON.stringify(event.body).payload

  let result = await client.create({ _type: 'job', ...payload })

  return {
    statusCode: 200,
    body: JSON.stringify(result),
  }
}

This should be parsing the event.body

Like

let payload = JSON.parse(event.body).payload

When I change it to .parse getting this error

Function invocation failed: SyntaxError: Unexpected token W in JSON at position 0

HI @jerome281 , did you include a payload object inside the body? do let payload = JSON.parse(event.body) and then print the payload and you’ll be able to see the shape of the object.

On my test form post-a-job.js I can console log the object data (see the attached image), I’m just struggling to pass it into my submission-created.js functions to push it into Sanity.io dataset.

When I run netlify dev and go to http://localhost:34567/submission-created getting “Function invocation failed: SyntaxError: Unexpected token W in JSON at position 0”

Hey @jerome281 did you resolve this? If not, where are you getting that error? If you can log the payload then you should be able to use it in your function. Can you clarify where you are getting the syntax error if you are able to log the payload?

Still having the issue, when I log all events in my function file http://localhost:34567/submission-created getting just this body ID

image

Hi,

Unless you need something other than the request body, I recommend logging just event.body. Also can you log it while it’s deployed in production and tell me if you see the correct data there? Thanks.