text: How to Use the JavaScript Fetch API to Perform HTTP Requests
  September 10th, 2021
  What You Will Learn in This Tutorial
  How to use the JavaScript fetch API to perform HTTP requests in the browser and in Node.js.
  Table of Contents
  Master Websockets — Learn how to build a scalable websockets implementation and interactive UI.
  Getting Started
  For this tutorial, we're going to use the CheatCode Next.js Boilerplate for showcasing usage of
  fetch on the client and the CheatCode Node.js Server Boilerplate for showcasing usage of
  fetch on the server.
  To get started, let's clone the Next.js boilerplate:
  Terminal
  git clone  client
  cd into the project and install it's dependencies:
  Terminal
  cd client && npm install
  After this, go ahead and start up the development server:
  Terminal
  npm run dev
  Next, in another tab or terminal window, we want to clone the Node.js boilerplate:
  Terminal
  git clone  server
  cd into the project and install the dependencies:
  Terminal
  cd server && npm install
  Before we start the development server, we need to install two additional dependencies:
  isomorphic-fetch and
  faker:
  Terminal
  npm i isomorphic-fetch faker
  With those two installed, go ahead and start up the server:
  Terminal
  npm run dev
  With that, we're ready to get started.
  Using the Fetch API in Node.js
  Though it may seem a bit backward, for this tutorial, we're going to start our work on the server-side and then move to the client. The reason why is that we're going to set up some test routes that we can perform
  fetch requests against on the client. While we're there, too, we'll take a quick look at how to use
  fetch in a Node.js server environment.
  /server/api/index.js
  import graphql from "./graphql/server"; export default (app) => { graphql(app); app.get("/users", (req, res) => { // We'll implement an HTTP GET test route here... }); app.post("/users", (req, res) => { // We'll implement an HTTP POST test route here... }); app.get("/photos", (req, res) => { // We'll implement a server-side fetch request here... }); };
  Inside of the Node.js boilerplate we cloned above, an Express.js server is already configured for us. In the file above, the boilerplate sets up the various APIs that it supports (by default, just a GraphQL API). Passed into the function being exported from this file is the Express
  app instance which is set up for us in the
  /index.js file in the project.
  Here, beneath the call to the function where we set up our GraphQL server
  graphql() (we won't use this, we're just calling it out to save confusion), we define three routes:
  /usersusing
  app.get()which creates an Express.js route that only accepts HTTP GET requests.
  /usersusing
  app.post()which creates an Express.js route that only accepts HTTP POST requests.
  /photosusing
  app.get()which an Express.js route that only accepts HTTP GET requests and will be where we use
  fetchto get data from a third-party API.
  /server/api/index.js
  import faker from "faker"; import graphql from "./graphql/server"; export default (app) => { graphql(app); app.get("/users", (req, res) => { const users = [...Array(50)].map(() => { return { name: { first: faker.name.firstName(), last: faker.name.lastName(), }, emailAddress: faker.internet.email(), address: { street: faker.address.streetAddress(), city: faker.address.city(), state: faker.address.state(), zip: faker.address.zipCode(), }, }; }); res.status(200).send(JSON.stringify(users, null, 2)); }); app.post("/users", (req, res) => { // We'll implement an HTTP POST test route here... }); app.get("/photos", (req, res) => { // We'll implement a server-side fetch request here... }); };
  Adding an
  import up top for the
  faker dependency we installed earlier, here, we're filling out the
  app.get() version of our
  /users route. Inside, our goal is to return some test data (we'll perform a
  fetch request from the client later and expect this data in return). For our data, we're using a little JavaScript trick.
  The
  [...Array(50)] that we're mapping over here is saying "create a new JavaScript array in memory with 50 elements in it (these will just be
  undefined values) and then 'spread' or 'unpack' that array—using the
  .map() method.
  ...spread operator—into the array wrapping that statement." Our goal here is to get 50 "placeholders" that we can replace using a JavaScript
  We see that happening here, returning an object describing a made-up user for each of the 50 placeholder elements. In turn, this will return us an array with 50 made-up user objects. To "make up" those users, we use the
  faker library—a tool for creating fake test data—to make a realistic test user for each iteration of our map (learn more about Faker's API here).
  Finally, after we've created our array of
  users, we take that variable and using the
  res object from Express.js (this is passed as the second argument to the callback function for our route), and do two things:
  - Set the HTTP status code to
  200using the
  .status()method (this is the standard HTTP code for "success").
  - Using the ability to "chain" methods, call to the
  .send()method after setting the
  .status()on
  res, passing in a stringified version of our
  usersvariable (containing our array of users).
  Here, using
  JSON.stringify() is necessary because only strings can be sent in response to HTTP requests. Later, on the client, we'll learn how to convert that string back into a JavaScript array.
  /server/api/index.js
  import faker from "faker"; import graphql from "./graphql/server"; export default (app) => { graphql(app); app.get("/users", (req, res) => { ... res.status(200).send(JSON.stringify(users, null, 2)); }); app.post("/users", (req, res) => { console.log(req.body); res.status(200).send(`User created!`); }); app.get("/photos", (req, res) => { // We'll implement a server-side fetch request here... }); };
  Next, for the
  app.post() version of our
  /users route, we keep things simple. Because the intent of an HTTP POST request is to create or insert some data into a database (or hand it off to another data source), here, we're just logging out the contents of
  req.body which is the parsed content sent to us via the request. This will come in handy later as we'll see how the options we pass to a
  fetch() request determine whether or not the body we pass on the client makes it to the server.
  Finally, here, we repeat the same pattern we saw in the
  app.get() version of
  /users, calling to
  res, setting the
  .status() to
  200, and sending back a string response (here, just a plain string signifying the receipt of the user).
  /server/api/index.js
  import faker from "faker"; import fetch from "isomorphic-fetch"; import graphql from "./graphql/server"; export default (app) => { graphql(app); app.get("/users", (req, res) => { ... res.status(200).send(JSON.stringify(users, null, 2)); }); app.post("/users", (req, res) => { console.log(req.body); res.status(200).send(`User created!`); }); app.get("/photos", (req, res) => { fetch(" async (response) => { const data = await response.json(); res.status(200).send(JSON.stringify(data.slice(0, 50))); } ); }); };
  For our final route, we create another
  app.get() route, this time using the route
  /photos. For this route, we're going to use a server-side
  fetch() call to a third-party API and send the data we get back to the client-side of our app. Up top, you can see that we've imported the
  isomorphic-fetch dependency we installed earlier as
  fetch.
  It's worth noting that there are multiple Node.js implementations of the
  fetch()API. While
  fetch()is built-in to modern web browsers, in Node.js, it needs to be polyfilled. Several packages have come up with their own solutions to do this, hence the variation. Another popular package you may see is
  node-fetch.
  Here, we make a call to the
  /photos endpoint on the free JSON Placeholder API which returns us an array of objects with pointers back to stock photographs.
  After our call to
  fetch(), we chain on a
  .then() callback—this signifies that we expect
  fetch() to return a JavaScript Promise—passing a function to that
  .then() method. Inside of that function, we take in the
  response to our request as an argument, also adding an
  async keyword before our function.
  We do this because on the next line, we make a call to
  await in front of a call to
  response.json(). The idea here is that
  response is not handed to us by
  fetch() in any specific format. Instead, we take the raw
  response and using one of a few methods on that
  response object, we convert the response into the format that we want/need.
  Here,
  response.json() is saying to convert the
  response into a JSON format. We use the
  await here because we expect
  response.json() (and its sibling methods like
  response.text()) to return a JavaScript Promise. With an
  await, we're saying "wait until this function has returned us a value that we can set to our
  data variable and then continue to the next line."
  On the next line, we see a familiar call to
  res.status(200).send(), making sure to
  JSON.stringify() our data before sending it back to the request made from the client side of our app.
  That does it for the server! Next, we're going to jump down to the client and see how
  fetch() works in the browser.
  Using the Fetch API in the browser
  Moving into the Next.js boilerplate we cloned earlier, to start, we're going to use the page-based routing feature of Next.js to create a new route on the client where we can test out our
  fetch() calls:
  /client/pages/index.js
  import React, { useState } from "react"; const Index = () => { const [data, setData] = useState([]); const getRequestWithFetch = (resource = "") => { // We'll make our GET requests using fetch here... }; const postRequestWithFetch = () => { // We'll make a our POST request using fetch here... }; return ( <div> <button className="btn btn-primary" style={{ marginRight: "10px" }} onClick={() => getRequestWithFetch("users")} > GET Request (Users) </button> <button className="btn btn-primary" style={{ marginRight: "10px" }} onClick={() => getRequestWithFetch("photos")} > GET Request (Photos) </button> <button className="btn btn-primary" onClick={postRequestWithFetch}> POST Request </button> <pre style={{ background: "#eee", marginTop: "20px", padding: "20px" }}> <code>{data}</code> </pre> </div> ); }; export default Index;
  In Next.js, pages (which are converted automatically to routes or URLs) are defined using React.js components. Here, we're using the function-based approach to defining a component in React which consists of a plain JavaScript function which returns some JSX markup (the markup language built for authoring components in React).
  In the body of that function, too, we can define other functions and make calls to a special type of function unique to React called hooks.
  Starting just inside the body of our function, we can see a call to one of these hook functions
  useState() (imported up top) which will allow us to set a dynamic state value and then access that value in our JSX markup and the other functions defined within our function component's body (a concept known as "closure functions," or, functions defined within functions in JavaScript).
  Here,
  useState([]) is saying "creating an instance of a state value, setting the default value to an empty array
  []."
  For the return value of that call, we expect to get back an array with two values: the first being the current value
  data and the second being a function we can use to update that value
  setData. Here, we use JavaScript array destructuring to access the contents of our array and simultaneously assign variables to the values at those positions in the array.
  To clarify that, if we wrote this line like
  const state = useState([]), we'd need to follow that line with something like:
  const data = state[0]; const setData = state[1];
  Using array destructuring, we can avoid this entirely.
  Jumping past our placeholder functions, next, looking at the JSX markup we're returning from our
  Index component function (what Next.js will render for our page), we can see that our actual UI is quite simple: we're rendering three buttons and a
  <pre></pre> block.
  The idea here is that we have one button for each of our
  fetch() request types, followed by a code block where we're rendering the response to each request (triggered by the button click). Here, we can see the
  data variable we "plucked off" using array destructuring from our call to
  useState() being passed into the
  <code></code> tag nested inside of our
  <pre></pre> tag. This is where we'll ultimately store the response data from our
  fetch() requests (and see that data on screen).
  Looking at each button, we can see the
  onClick attribute being assigned a value. For the first two buttons—which we'll be responsible for performing our
  GET request examples—we call to the function defined above
  getRequestWithFetch(), passing in a string describing the resource or path that we'd like to call to (this will make more sense in a bit).
  For the last button, we just pass the function
  postRequestWithFetch directly as we do not need to pass any arguments when we call that function.
  /client/pages/index.js
  import React, { useState } from "react"; const Index = () => { const [data, setData] = useState([]); const getRequestWithFetch = (resource = "") => { fetch(` { credentials: "include", }).then(async (response) => { const data = await response.json(); // NOTE: Doing JSON.stringify here for presentation below. This is not required. setData(JSON.stringify(data, null, 2)); }); }; const postRequestWithFetch = () => { // We'll make a our POST request using fetch here... }; return ( <div> ... </div> ); }; export default Index;
  Looking at the
  getRequestWithFetch function we hinted at below, we can see the string we passed for our resource name being defined as the argument
  resource on our function. Inside of that function, we set up our call to
  fetch(). Something you'll notice is that, unlike on the server, we're not importing
  fetch() from anywhere.
  This is because
  fetch is built-in to modern browsers as a global value (meaning it's automatically defined everywhere in the browser).
  Looking at our call, just like we saw earlier, we call to
  fetch() passing a URL as the first argument. In this case, we're passing the URL for one of the
  GET routes we defined on our server earlier. This will dynamically change based on the value passed for
  resource, to either
   or
  
  As the second argument to
  fetch(), we pass an options object. Here, we're just passing a single property
  credentials: "include". As we'll see when we implement our POST request, what we pass here determines how our request actually behaves. In this case, we're telling
  fetch() to include the browser's cookies in the request headers when it sends the request. Though we're not authenticating our requests on the server this is important to be aware of if you expect
  fetch() to behave like a browser (which automatically sends the cookies with its own requests).
  Finally, here, down in the
  .then() callback (remember,
  fetch() will return us a JavaScript Promise), we use the async/await pattern to
  await response.json() to get the return data back in a JavaScript-friendly format—array or object—and then call to the
  setData() function we got back from our
  useState() hook function to set the response data for display down in our
  <pre></pre> tag.
  ;
  Next, for our
  postRequestWithFetch() function, we repeat a similar process as our GET request. Here, though, we hardcode our URL (we only have one POST route on the server) and, because we're doing a request other than a GET, we set a
  method option to
  POST. If we do not do this,
  fetch() will assume we're trying to perform a GET request or "fetch" some data.
  Below this, we can see the same
  credentials: "include" as our GET request (again, purely for awareness here). Next, the important part, because this is a POST request, we add a
  body option set to a stringified JavaScript object with some test data on it. Remember, HTTP requests can only pass strings back and forth. To make this work, in the
  headers option, we add the HTTP
  Content-Type header, setting it to
  application/json. This is important. This communicates to the server that the data we're sending in the body should be parsed as JSON data.
  ()(req, res, next); };
  To make sense of this, quickly, on the server-side of our app, the Node.js boilerplate we're using has something known as a
  middleware function which is run whenever a request comes into the server, just before it's handed off to our Express.js routes. Here, we can see at the bottom of the middleware function that parses the HTTP request body to a JSON format.
  If we didn't set the
  Content-Type header in our
  fetch() request back on the client, our request body (
  req.body in our route handler on the server) would be an empty object. Once we set this header, however, the server responding to our request knows "what to do" and receives our request body as intended.
  ;
  Focusing back on our
  postRequestWithFetch function on the client, in the
  .then() callback, we use a similar flow to what we saw before with async/await, however this time, instead of
  response.json() we use
  response.text(). This is because the response we send back from the server for our POST request is just a plain string (as opposed to a stringified object like in our other requests). Once we've got our
  data, we pop it on to state with
  setData().
  That's it! Now we're ready to take this for a spin:
  Wrapping up
  In this tutorial, we learned how to perform HTTP requests using the JavaScript
  fetch() API. We started on the server, defining routes to send our requests to from the client, also learning how to use
  fetch() via the
  isomorphic-fetch library from within Node.js. Next, on the client, we learned how to run HTTP GET and POST requests, learning about the proper options to pass to ensure our server can understand our request.
  Get the latest free JavaScript and Node.js tutorials, course announcements, and updates from CheatCode in your inbox.
  No spam. Just new tutorials, course announcements, and updates from CheatCode.