Services And The Fetch Api

The Fetch API returns a Response, not data. The "services" pattern separates what fetches from what stores. Here is how to structure the data layer of a vanilla JS app.

May 1, 20263 min read11 / 12

The thing that surprised me most about fetch is that it does not give you data. It gives you a Response. That two-step structure -- wait for headers, then wait for the body -- tripped me up the first time. Once I understood why it works that way, the whole data layer started to make sense.

The Essentials

  1. fetch returns a Response: Not the data you requested. You get the data by calling .json() (or .text(), .blob()) on the response. Both are async operations.
  2. async/await with fetch: await fetch(url) gives you the response. await response.json() gives you the parsed data.
  3. Services pattern: A separation of concerns between fetching (API service) and storing (Store service). Neither knows about the other's implementation.
  4. Singleton objects: A plain JavaScript object can serve as a singleton without needing a class definition. One object, one instance, no constructor ceremony.
  5. null vs empty array for initial state: Use null for data that has not loaded yet. Use [] for a collection that exists but has nothing in it yet.

The fetch API

fetch is the browser's built-in HTTP client. It returns a Promise that resolves to a Response object:

JavaScript
const response = await fetch('/data/menu.json'); const data = await response.json();

There are two await calls here. The first waits for the HTTP response headers to arrive (the Response object is available as soon as headers come back, before the body has fully downloaded). The second waits for the body to download and be parsed as JSON.

This two-step structure surprises people the first time. You cannot do const data = await fetch(url) and expect the parsed JSON. You always go through the Response first.

The API Service

The API service is an object responsible for all network requests. For a simple app this might be a single function:

JavaScript
// services/API.js const API = { url: '/data/menu.json', async fetchMenu() { const response = await fetch(this.url); return response.json(); } }; export default API;

This object knows where data comes from and how to parse it. It does not know what happens to the data after it is returned. That is someone else's responsibility.

The Store Service

The Store service holds the app's in-memory state. It does not know where data comes from:

JavaScript
// services/Store.js const Store = { menu: null, // null = not loaded yet cart: [] // empty array = cart exists, nothing in it yet }; export default Store;

The distinction between null and [] is intentional. menu: null means the data has not been requested or has not returned yet. menu: [] would mean the data returned and was empty. These are different states and deserve different values.

Keeping Responsibilities Separate

A common mistake is to have the fetch logic write directly to the store, creating a coupling between two concerns that should be independent:

JavaScript
// Tightly coupled - the API service knows about the Store const API = { async fetchMenu() { const data = await fetch('/data/menu.json').then(r => r.json()); Store.menu = data; // This API module now depends on Store } };

A cleaner approach is to return the data and let the caller decide what to do with it:

JavaScript
// Decoupled - API only fetches and returns const API = { async fetchMenu() { const response = await fetch('/data/menu.json'); return response.json(); } }; // The caller (e.g., menu.js) decides where the data goes async function loadData() { const menu = await API.fetchMenu(); Store.menu = menu; }

Now the API module can be reused or tested without any dependency on the Store. The Store can be changed without touching the API service.

Further Reading and Watching

  • MDN: Using Fetch - The full guide to fetch, including error handling, request configuration, and the two-step response model.
  • MDN: Response.json - Reference for parsing the response body as JSON.

Video:

  • JavaScript Fetch API by Traversy Media. Walks through fetch, .then, and async/await patterns with practical examples.