How to encode and parse / decode a nested query string Javascript

1.5k Views Asked by At

I'm sending form data from React Hook Form to Netlify via their submission-created function. I don't have any problem with encoding individual form field values, but now I'm trying to encode an array of objects.

Here is an example of my form data:

{
  _id: "12345-67890-asdf-qwer",
  language: "Spanish",
  formId: "add-registration-form",
  got-ya: "",
  classType: "Private lessons",
  size: "1",
  days: [
    {
      day: "Monday",
      start: 08:00",
      end: "09:30"
    },
    {
      day: "Wednesday",
      start: "08:00",
      end: "09:30"
    }
  ]
}

The only problem I have is with the "days" array. I've tried various ways to encode this and this is the function I've currently been working with (which isn't ideal):

 const encode = (data) => {
    return Object.keys(data).map(key => {
      let val = data[key]
      if (val !== null && typeof val === 'object') val = encode(val)
      return `${key}=${encodeURIComponent(`${val}`.replace(/\s/g, '_'))}`
    }).join('&')
  }

I tried using a library like qs to stringify the data, but I can't figure out how to make that work.

And here is the function posting the data to Netlify:

// Handles the post process to Netlify so I can access their serverless functions
   const handlePost = (formData, event) => {
    event.preventDefault()
    fetch(`/`, {
      method: "POST",
      headers: { "Content-Type": "application/x-www-form-urlencoded" },
      body: encode({ "form-name": 'add-registration-form', ...formData }),
    })
      .then((response) => {
        if(response.status === 200) {
          navigate("../../")
        } else {
          alert("ERROR!")
        }
        console.log(response)
      })
      .catch((error) => {
        setFormStatus("error")
        console.log(error)
      })
  }

Finally, here is a sample of my submission-created file to receive and parse the encoded data:

const sanityClient = require("@sanity/client")
const client = sanityClient({
  projectId: process.env.GATSBY_SANITY_PROJECT_ID,
  dataset: process.env.GATSBY_SANITY_DATASET,
  token: process.env.SANITY_FORM_SUBMIT_TOKEN,
  useCDN: false,
})

const { nanoid } = require('nanoid');

exports.handler = async function (event, context, callback) {
  
  // Pulling out the payload from the body
  const { payload } = JSON.parse(event.body)

  // Checking which form has been submitted
  const isAddRegistrationForm = payload.data.formId === "add-registration-form"

  // Build the document JSON and submit it to SANITY
  if (isAddRegistrationForm) {


    // How do I decode the "days" data from payload?
    let schedule = payload.data.days.map(d => (
    {
      _key: nanoid(),
      _type: "classDayTime",
      day: d.day,
      time: {
        _type: "timeRange",
        start: d.start,
        end: d.end
      }
    }
  ))


    const addRegistrationForm = {
      _type: "addRegistrationForm",
      _studentId: payload.data._id,
      classType: payload.data.classType,
      schedule: schedule,
      language: payload.data.language,
      classSize: payload.data.size,
    }
    const result = await client.create(addRegistrationForm).catch((err) => console.log(err))
  }
  
  callback(null, {
    statusCode: 200,
  })
}

So, how do I properly encode my form data with a nested array of objects before sending it to Netlify? And then in the Netlify function how do I parse / decode that data to be able to submit it to Sanity?

1

There are 1 best solutions below

0
On BEST ANSWER

So, the qs library proved to be my savior after all. I just wasn't implementing it correctly before. So, with the same form data structure, just make sure to import qs to your form component file:

import qs from 'qs'

and then make your encode function nice and succinct with:

     // Transforms the form data from the React Hook Form output to a format Netlify can read
      const encode = (data) => {
        return qs.stringify(data)
      } 

Next, use this encode function in your handle submit function for the form:

 // Handles the post process to Netlify so we can access their serverless functions
   const handlePost = (formData, event) => {
    event.preventDefault()

    fetch(`/`, {
      method: "POST",
      headers: { "Content-Type": "application/x-www-form-urlencoded" },
      body: encode({ "form-name": 'add-registration-form', ...formData }),
    })
      .then((response) => {
        reset()
        if(response.status === 200) {
          alert("SUCCESS!")
        } else {
          alert("ERROR!")
        }
        console.log(response)
      })
      .catch((error) => {
        console.log(error)
      })
  }

Finally, this is what your Netlify submission-created.js file should look like more or less:

const sanityClient = require("@sanity/client")
const client = sanityClient({
  projectId: process.env.GATSBY_SANITY_PROJECT_ID,
  dataset: process.env.GATSBY_SANITY_DATASET,
  token: process.env.SANITY_FORM_SUBMIT_TOKEN,
  useCDN: false,
}) 
const qs = require('qs')
const { nanoid } = require('nanoid');

exports.handler = async function (event, context, callback) {
  
  // Pulling out the payload from the body
  const { payload } = JSON.parse(event.body)


  // Checking which form has been submitted
  const isAddRegistrationForm = payload.data.formId === "add-registration-form"

  // Build the document JSON and submit it to SANITY
  if (isAddRegistrationForm) {
    const parsedData = qs.parse(payload.data)
    
    let schedule = parsedData.days
      .map(d => (
        {
          _key: nanoid(),
          _type: "classDayTime",
          day: d.day,
          time: {
            _type: "timeRange",
            start: d.start,
            end: d.end
          }
        }
      ))

    const addRegistrationForm = {
      _type: "addRegistrationForm",
      submitDate: new Date().toISOString(),
      _studentId: parsedData._id,
      classType: parsedData.classType,
      schedule: schedule,
      language: parsedData.language,
      classSize: parsedData.size,
    }
    const result = await client.create(addRegistrationForm).catch((err) => console.log(err))
  }
  
  callback(null, {
    statusCode: 200,
  })
}