Staticman: POST to API endpoint with fetch()

Created on 20 Jan 2019  路  5Comments  路  Source: eduardoboucas/staticman

Hi everyone :wave:

I'm implementing Staticman on my GatsbyJS site, and it's looking great!

The only thing is that I want my comment submission event to adapt to React (no page reload for starters), so I'm turning action="https://dev.staticman.net/..." method="POST" into a handleSubmit function that POSTs the event via fetch():

async handleSubmit(event) {
  event.preventDefault() // stop the form from submitting

  const request = await fetch("https://dev.staticman.net/v3/entry/github/robinmetral/eaudepoisson/master/comments", {
    method: "POST",
    body: event,
  })

  const response = await request.json()
  console.log(response)
}

However I get this error as a response:

errorCode: "MISSING_PARAMS"
data: [fields]
success: false

Staticman doesn't recognize the required fields in my event object. I've also tried to turn the object into a JSON string, but I've had the same error.

Is the API endpoint only allowing the x-www-form-urlencoded POST format? Has anyone been using fetch() to POST to Staticman?

Most helpful comment

After some research I've found a solution. Here's my handleSubmit function:

handleSubmit = async (event) => {
  event.preventDefault()

  // extract form data
  const formdata = new FormData(event.target)

  // convert FormData to json object
  // SOURCE: https://stackoverflow.com/a/46774073
  const json = {}
  formdata.forEach(function(value, prop){
    json[prop] = value
  })

  // convert json to urlencoded query string
  // SOURCE: https://stackoverflow.com/a/37562814 (comments)
  const formBody = Object.keys(json).map(key => encodeURIComponent(key) + '=' + encodeURIComponent(json[key])).join('&')

  // POST the request to Staticman's API endpoint
  const response = await fetch("https://dev.staticman.net/v3/entry/github/robinmetral/eaudepoisson/master/comments", {
    method: "POST",
    headers: {"Content-Type": "application/x-www-form-urlencoded"},
    body: formBody,
  })
    .then(response => {
      // reset form
      document.getElementById("comment-form").reset()
      // display success message
      document.getElementById("success").style.display = "block"
    })
    .catch(error => {
      console.log(error)
      document.getElementById("failure").style.display = "block"
    })
}

This is what it does:

  1. it extracts the submitted data as a FormData object
  2. it converts the FormData to a JSON object, using a snippet found on SO
  3. it converts the JSON to the expected application/x-www-form-urlencoded content-type or "query string", using another SO snippet
  4. it sends the request to the API endpoint, catches errors and displays a success message

It's probably not the only solution, and if you see something that I could improve, please let me know below or send me a PR!

All 5 comments

After some research I've found a solution. Here's my handleSubmit function:

handleSubmit = async (event) => {
  event.preventDefault()

  // extract form data
  const formdata = new FormData(event.target)

  // convert FormData to json object
  // SOURCE: https://stackoverflow.com/a/46774073
  const json = {}
  formdata.forEach(function(value, prop){
    json[prop] = value
  })

  // convert json to urlencoded query string
  // SOURCE: https://stackoverflow.com/a/37562814 (comments)
  const formBody = Object.keys(json).map(key => encodeURIComponent(key) + '=' + encodeURIComponent(json[key])).join('&')

  // POST the request to Staticman's API endpoint
  const response = await fetch("https://dev.staticman.net/v3/entry/github/robinmetral/eaudepoisson/master/comments", {
    method: "POST",
    headers: {"Content-Type": "application/x-www-form-urlencoded"},
    body: formBody,
  })
    .then(response => {
      // reset form
      document.getElementById("comment-form").reset()
      // display success message
      document.getElementById("success").style.display = "block"
    })
    .catch(error => {
      console.log(error)
      document.getElementById("failure").style.display = "block"
    })
}

This is what it does:

  1. it extracts the submitted data as a FormData object
  2. it converts the FormData to a JSON object, using a snippet found on SO
  3. it converts the JSON to the expected application/x-www-form-urlencoded content-type or "query string", using another SO snippet
  4. it sends the request to the API endpoint, catches errors and displays a success message

It's probably not the only solution, and if you see something that I could improve, please let me know below or send me a PR!

This worked for me! Thanks @robinmetral for this solution

Glad to see that posting solutions in here is useful @kyrelldixon :slightly_smiling_face: Happy commenting!

@robinmetral thanks a lot, it's worked for me, but, what was driving me nuts was this

Step 3. Hook up your forms
Forms should POST to:
https://api.staticman.net/v2/entry/{GITHUB USERNAME}/{GITHUB REPOSITORY}/{BRANCH}/{PROPERTY (optional)}

But looking at your code (apart from dev and v3), at the end it seems that should post there:
https://dev.staticman.net/v3/entry/github/{GITHUB USERNAME}/{GITHUB REPOSITORY}/{BRANCH}/{PROPERTY (optional)}

So, in my hands, this has worked for me (with semantic-ui):

<Form onSubmit={onSubmit}>
    <Form.Input name="name" onChange={changeUserName} placeholder="what" />
    <Form.TextArea name="message" onChange={changeuserMessage} placeholder="what2" />
    <Form.Button>Submit</Form.Button>
</Form>

and

  const [userName, setUserName] = useState(() => '')
  const [userMessage, setUserMessage] = useState(() => '')
  const wait = ms => new Promise((r, j) => setTimeout(r, ms))

  let changing = 0 // to create a buffer to avoid changing state always, the browser gets slow otherwise
  const changeUserName = async (e, { value }) => {
    changing++
    let prev_changing = changing
    await wait(100)
    if (prev_changing === changing) setUserName(value)
  }

  const changeuserMessage = async (e, { value }) => {
    changing++
    let prev_changing = changing
    await wait(100)
    if (prev_changing === changing) setUserMessage(value)
  }

  const onSubmit = async e => {
    e.preventDefault()
    const formdata = new FormData()
    formdata.set('fields[name]', userName)
    formdata.set('fields[message]', userMessage)
    const json = {}
    formdata.forEach((value, prop) => (json[prop] = value))
    const formBody = Object.keys(json)
      .map(key => encodeURIComponent(key) + '=' + encodeURIComponent(json[key]))
      .join('&')

    // in the repo, create a folder named 'comments'
    const response = await fetch(
      'https://dev.staticman.net/v3/entry/github/{my github user}/{my repo}/master/comments/',
      {
        method: 'POST',
        headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
        body: formBody,
      }
    )
    console.log(response)
  }

The fetch method doesn't support IE. It can be rewritten with the send method in xmlHttpRequest.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

BloggerBust picture BloggerBust  路  6Comments

taxilly picture taxilly  路  15Comments

fernandoportelab picture fernandoportelab  路  3Comments

eduardoboucas picture eduardoboucas  路  11Comments

onwingslikeeagles picture onwingslikeeagles  路  15Comments