The JavaScriptfetch
API simplifies making HTTP requests like GET and POST between clients (e.g. browsers) and servers. It replaces older, more complex methods like XMLHttpRequest
and jQuery’s AJAX. This guide covers:
.then()
and async/await
Whether you're sending form data or fetching Star Wars characters, fetch
makes it clean, modern, and asynchronous—perfect for interactive web apps.
We at Codesmith cultivate technologists who are at the intersection of society and tech, able to meet this moment and thrive in an ever-changing world. Ready to become a modern software engineer?
What is commonly referred to as the JavaScript fetch API is a browser feature that simplifies the process of making HTTP requests between clients and servers. So, what exactly is an HTTP request and why has fetch become the go-to tool for making them? In this article, we’re going to break that down! We will explore:
Before we can understand what a fetch request is and how to make one, we need to understand what an HTTP (and HTTPS) request is. HTTP stands for HyperText Transfer Protocol. It’s the set of rules that govern how data is transferred between clients (ie: your browser) and servers, and serves as the foundation of the internet as it makes sending and receiving data possible. HTTPS (HTTP + SECURITY) simply adds a layer of security to these requests. It just means that the browser and server create an encrypted (secure/protected) connection before they start sending information back and forth. Here’s how HTTP works when you visit a website:
Below is an example of a basic HTTP GET request.
There are several different types of requests that can be made with the HTTP protocol – referred to as methods. It's important to note that regardless of the HTTP method used to make the request every request will always receive a response – even if the request is unsuccessful. The most common types are:
GET When a client (such as the browser) requests data from a web server. For example, when visiting a webpage, the browser sends a GET request to a server for the content of the page.
POST When a client sends data to a web server. For example, submitting a form on a website. Let’s say a user wants to sign in to their account. They type in their username, password and click “send”. This triggers a post request website’s server.
PUT When a client replaces data that is already on a web server. For example, a user wants to change their profile on a website. They upload the new photo, replace their email address, change their age, etc. and click “save”. The browser (the client) then makes a PUT request to the server to replace the entire profile.
PATCH When a client partially replaces data on a server. For example, a user updating their email address in their profile. The browser sends a PATCH request to replace only the email address and nothing else in the profile.
DELETE When a client removes data on a server. For example a user deleting their profile.
Before the release of fetch in 2015, HTTP requests in JavaScript had to be made using XMLHttpRequest. This could be done one of two ways:
In 2015 fetch was released as a native browser feature that allows us to use JavaScript to make asynchronous HTTP requests (like the two above) without the confusing syntax of the XMLHttpRequest object or having to import external libraries like JQuery. As we know from 10 Techniques to Optimize your JavaScript Code, importing fewer libraries and dependencies in your application makes it lighter, allowing it to run faster. In a moment, we’ll see just how flexible and straightforward this API can be.
Let’s take a look at building out asynchronous HTTP requests and handling HTTP responses using the JavaScript fetch API.
The main function of the JavaScript fetch API is, of course, fetch which takes in two main parameters:
Let’s take a closer look at how these two parameters are structured and passed into the JavaScript fetch API for a POST request:
In the example above, we use the postRequest object to provide details about the request, like the HTTP method, headers, and body. This additional information tells the server how to process our request. Once postRequest is built out, we can pass it and the URL into the fetch method!
Let’s take a quick moment to talk about headers and why we need them. The headers above are request headers because we are making a request. Later on, we’ll take a look at response headers. Request headers contain additional information about the request to help the server understand how to handle our request properly. Some common headers are:
In the postRequest object below, the value of body is the data we’d like to send to the URL. But why do we need to JSON.stringify(bodyData) instead of assigning body to bodyData directly? And why did we set the ‘Content-Type’ property in ‘headers’ to ‘application/json’?
Today, data is commonly passed between clients and servers in the form of JSON because it’s lightweight, easily parsed, and supported by most programming languages. This is why we turn bodyData into a JSON string before sending our request. The body can also be sent as XML, form data, plain text, or binary formats which we won’t get into in this article.
The server we are sending this data to expects a JSON format and Content-Type: application/json header in our request tells the server that we are, in fact, sending the data as JSON, so it can process it correctly.
Now we know how to make a POST request using the JavaScript fetch API! But what if, instead of making a fancy POST request, you only want to make a humble GET request? If so, good news! GET requests don’t typically require an options object. This is because invoking fetch() defaults to GET requests when no options are provided. Let’s take a look at making a simple GET request to the Star Wars API.
Some APIs require an api_token in order to access certain resources. This can often be appended to the URL as a query parameter as seen below using a ‘?’ followed by api_token (our parameter) and your token:
However, not all APIs allow tokens to be sent as query parameters as seen above. Instead, they may need to be sent in request headers as bearer tokens. In this case, our GET request will look very similar to our POST request above. Take a look at the example below. Look familiar?
As mentioned before, every HTTP request must receive a response. After a client sends an HTTP request to a server–like we’re doing above by invoking fetch(url, getRequestBody), the server processes the request and sends back an HTTP response. This response acts as the server's reply, either providing the requested information or explaining why the request couldn’t be completed. Knowing the structure of an HTTP response is key to understanding how data moves between the client and the server. While you can view this process in the Developer Tools of your browser, we used Postman to visualize it in this article, and recommend that you try it out. This way, you can follow along without needing to set up your own app!
If you would like to observe this process for yourself, you can download Postman and send a GET request to the URL we used above (‘https://swapi.dev/api/people/1’).
Postman automatically separates and organizes each part of the HTTP response into different tabs for readability. In the example above you can see the response Headers we’ve received from our request, but if you want to see the raw response data (the actual HTTP response), you can check it out in the Postman Console. Here’s an example of the raw log we received from our GET request to the URL above:
The first line of an HTTP response is the status line, which includes three parts:
- 200 Indicates that the request was successful.
- 404 Indicates that the resource was not found.
- 500 Indicates an internal server error.
Check out a list of status codes and their meanings with Status Dogs.
After the status line comes response headers, which provide additional details about the response. There are many types of response headers, and for the purposes of this article we won’t be covering them all, but if you’re curious about them you can check out this link: MDN Response-headers. Some of the headers we’ve received include:
Server: Tells the client which server software is handling the request.
Date: Tells the client the exact date and time at which the response was generated.
Content-Type: Tells the client how the response body is formatted. In this example it’s specified that the body is provided in JSON format. Sound familiar?
Connection: This tells the client how the network connection between the client and the server should be handled after the current request is completed. In this example, “keep-alive” means that the server will keep the connection open for future requests.
After the headers section in an HTTP response there will be one blank line, followed by the response body on the next line as seen in the raw data example above. The body of a response contains the actual data being sent back by the server. Most developer tools for inspecting these responses will have separate display sections for the headers and the body, so we’ve separated them here for readability.
In our next example image, we can see in the body tab of our Postman app that we have an object containing metadata for a specified star-wars character. This is the response body! Since the Content-Type is application/json, this response body contains data in JSON-format:
If something had gone wrong with the request, the body might include error details instead of the requested data. For example:
Once the client receives the response, it will process the status line, headers, and body to perform the necessary actions. For example, the JSON data in the body might be used to update the content on a webpage, such as displaying additional information about Luke Skywalker.
Promise chaining with JavaScript fetch API
By understanding each part of an HTTP response, now we can dive into how to handle the response that will come back from our fetch request! Below, we see how promise chaining has been implemented to handle a successful call to the Star Wars API using .then() and .catch().
In the example above the green arrow represents the Thread of Execution:
The parameter in each invocation of .then() is a callback function that handles the Promise returned from the previous .then(). Because of this we must return the resulting promise from each function so it can be handled in the next .then(). If you are using object literals (curly braces), you’ll need to include the return keyword. For more information on Promises check out this article on Understanding Promises.
With the built-in asynchronous functionality of the Javascript fetch API, the code can continue running while waiting for the network request to finish, without blocking the page. It ensures that users can still interact with the page (like clicking or scrolling), see updates, or use other features while the request is being processed in the background. Without this asynchronous functionality, our entire application would freeze until it received a response to the HTTP request.
Many engineers prefer to handle Promise chaining their HTTP responses using async/await instead of .then(). This is totally fine and really just a matter of personal preference. Below is an example of the same functionality using async/await which is designed for readability by making asynchronous code look more like synchronous code.
If the order of operations in these examples looks a little funny to you or you're unfamiliar with Promises, no worries! Be sure to take a look at Codesmith’s Hard Parts lecture on Promises, Async and the Event Loop.
HTTP/HTTPS requests make it possible to send and receive resources (data) between clients (ie: your browser) and servers (ie: where the Starwars API is hosted). Every request must receive a response, even if the request was unsuccessful.
The JavaScript fetch API is a powerful tool that not only makes it possible to run HTTP requests asynchronously, but also abstracts away the confusing syntax of older methods like XMLHttpRequest or libraries such as JQuery. The fetch API uses Promises, enabling us to implement chaining with .then or async/await improving readability even further. This gives us complete control over the data we are sending/receiving using fetch. Now that you know how to use fetch, you'll be sending and receiving data over the internet like tossing a stick in the park for a well-trained pup. Who's a good boy?? Happy fetching!!
Explore CS Prep further in our beginner-friendly program.
Get more free resources and access to coding events every 2 weeks.
Connect with one of our graduates/recruiters.
Our graduates/recruiters work at:
Sam and Alex are writing partners, content creators, proud Codesmith alumni, and best friends. Former professional dancers/actors, the two transitioned into software development from musical theatre to bring their creative energy and passion for storytelling into the world of tech — specifically in developer relations. Their mission is simple: make software development fun, accessible, and community-driven. Through comedy, empathy, and a deep understanding of what makes content resonate, Alex and Sam strive to build bridges between developers and the tools they love. Sam, a Lead Engineering Fellow at Codesmith, is known among students and colleagues for his empathetic teaching style, strong sense of responsibility toward his students, and infectious sense of humor. Alex, a Technical Content Creator at Codesmith, blends technical knowledge with storytelling and humor to turn complex topics into engaging, easy-to-understand content.
Connect with one of our recruiters to learn about their journeys.
Our graduates/recruiters work at: