Javascript Fetch Pitfall: json.parse() vs. response.json()

01/31/20191 Min Read — In JavaScript

Request/AJAX

Yesterday I was writing a function for an API call. The documentation gave an example for Node.js using the request module:

var request = require("request");
[...]
request({
    uri: "https://api.rebrandly.com/v1/links",
    method: "POST",
    body: JSON.stringify({
          destination: "https://www.youtube.com/channel/UCHK4HD0ltu1-I212icLPt3g"
        , domain: { fullName: "rebrand.ly" }
      //, slashtag: "A_NEW_SLASHTAG"
      //, title: "Rebrandly YouTube channel"
    }),
    headers: {
      "Content-Type": "application/json",
      "apikey": "YOUR_API_KEY",
      "workspace": "YOUR_WORKSPACE_ID"
    }
  }, function(err, response, body) {
    var link = JSON.parse(body);
    console.log("Long URL was "+link.destination+", short URL is "+link.shortUrl);
  }

request is asynchronous and uses streaming and callbacks. When the function is done, it doesn't return anything, it calls back. You then use JSON.parse() to create a Javascript object and do something useful with it.

MDN Documentation for JSON.parse():

The JSON.parse() method parses a JSON string, constructing the JavaScript value or object described by the string.

Fetch

I rewrote the API call using async/await and fetch. The response of the fetch call is a Javascript Promise. You have to parse it to make it useful.

const apiKey = process.env.REACT_APP_REBRANDLY_API_KEY

export const postUrl = async inputUrl => {
  try {
    const config = {
      method: 'POST',
      headers: {
        Accept: 'application/json',
        'Content-Type': 'application/json',
        apiKey: apiKey,
      },
      body: JSON.stringify({
        destination: inputUrl,
        domain: { fullName: 'rebrand.ly' },
      }),
    }
    const url = 'https://api.rebrandly.com/v1/links'
    const rawResponse = await fetch(url, config)
    const response = await rawResponse.json()
    if (rawResponse.ok) {
      const { destination, shortUrl } = response
      return { destination, shortUrl }
    }
  } catch (error) {
    console.log(`Something went wrong: ${error}`)
    return null
  }
}

But you have to use the json() method of the fetch API to parse it - not JSON.parse()!

MDN Documentation for json():

The json() method of the Body mixin takes a Response stream and reads it to completion. It returns a promise that resolves with the result of parsing the body text as JSON.

tl;dr

AJAX works with callbacks, fetch with Promises.
Use JSON.parse() to parse the response for AJAX. Use json() to parse the response for fetch.

Further Reading