React + Fetch - Fake Backend Example for Backendless Development
Built with React 16.13 and the Fetch API
Other versions available:
- Angular: Angular 14, 10, 9, 8, 7, 6, 5, 2
- ASP.NET Core: Blazor WebAssembly
The following is an example of how to implement a fake or mock backend in a React app that uses the Fetch API (fetch()
) for performing HTTP requests. The Fetch API comes bundled with all modern browsers.
What is a fake backend?
A fake backend is used for doing backendless development in React which allows you to demo your code without the need to create a real backend server api, it's perfect for sharing code in places like StackBlitz which doesn't have a backend, or when you're developing a front end before the backend is available.
I created the below fake backend as part of a React Hooks + Redux Authentication Tutorial, it intercepts specific routes to handle authentication and user management, and uses browser local storage to save data so it can behave just like a real api including data persistance. Any routes that aren't handled by the fake backend get passed through to the real backend api.
Monkey patching fetch to intercept React HTTP requests
Monkey patching is a technique used to alter the behaviour of an existing function, either to extend it or change the way it works. In JavaScript this is done by storing a reference to the original function in a variable and replacing the original function with a new custom function that (optionally) calls the original function before/after executing some custom code.
React + Fetch Fake Backend
The React fake backend is enabled by executing the below configureFakeBackend()
function which monkey patches the fetch()
.
The new custom fetch function returns a javascript Promise
that resolves after a half second delay to simulate a real api call. The fake backend is organised into a top level handleRoute()
function that checks the request url and method to determine how the request should be handled. For intercepted routes one of the below // route functions
is called, for all other routes the request is passed through to the real backend via the realFetch()
function which points to the original window.fetch
function. Below the route functions there are a few // helper functions
for returning different response types and performing small tasks.
// array in local storage for registered users
let users = JSON.parse(localStorage.getItem('users')) || [];
export function configureFakeBackend() {
let realFetch = window.fetch;
window.fetch = function (url, opts) {
const { method, headers } = opts;
const body = opts.body && JSON.parse(opts.body);
return new Promise((resolve, reject) => {
// wrap in timeout to simulate server api call
setTimeout(handleRoute, 500);
function handleRoute() {
switch (true) {
case url.endsWith('/users/authenticate') && method === 'POST':
return authenticate();
case url.endsWith('/users/register') && method === 'POST':
return register();
case url.endsWith('/users') && method === 'GET':
return getUsers();
case url.match(/\/users\/\d+$/) && method === 'DELETE':
return deleteUser();
default:
// pass through any requests not handled above
return realFetch(url, opts)
.then(response => resolve(response))
.catch(error => reject(error));
}
}
// route functions
function authenticate() {
const { username, password } = body;
const user = users.find(x => x.username === username && x.password === password);
if (!user) return error('Username or password is incorrect');
return ok({
id: user.id,
username: user.username,
firstName: user.firstName,
lastName: user.lastName,
token: 'fake-jwt-token'
});
}
function register() {
const user = body;
if (users.find(x => x.username === user.username)) {
return error(`Username ${user.username} is already taken`);
}
// assign user id and a few other properties then save
user.id = users.length ? Math.max(...users.map(x => x.id)) + 1 : 1;
users.push(user);
localStorage.setItem('users', JSON.stringify(users));
return ok();
}
function getUsers() {
if (!isLoggedIn()) return unauthorized();
return ok(users);
}
function deleteUser() {
if (!isLoggedIn()) return unauthorized();
users = users.filter(x => x.id !== idFromUrl());
localStorage.setItem('users', JSON.stringify(users));
return ok();
}
// helper functions
function ok(body) {
resolve({ ok: true, text: () => Promise.resolve(JSON.stringify(body)) });
}
function unauthorized() {
resolve({ status: 401, text: () => Promise.resolve(JSON.stringify({ message: 'Unauthorized' })) });
}
function error(message) {
resolve({ status: 400, text: () => Promise.resolve(JSON.stringify({ message })) });
}
function isLoggedIn() {
return headers['Authorization'] === 'Bearer fake-jwt-token';
}
function idFromUrl() {
const urlParts = url.split('/');
return parseInt(urlParts[urlParts.length - 1]);
}
});
}
}
Hooking up the fake backend to your React App
To add the fake backend to your React project you just need to import the configureFakeBackend
function in your main React index.js/index.jsx file and execute it as shown below on lines 9
and 10
.
This is the complete main React index.jsx
from the React Hooks example where the fake backend is used, the full tutorial is available here.
import React from 'react';
import { render } from 'react-dom';
import { Provider } from 'react-redux';
import { store } from './_helpers';
import { App } from './App';
// setup fake backend
import { configureFakeBackend } from './_helpers';
configureFakeBackend();
render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById('app')
);
Need Some React Help?
Search fiverr for freelance React developers.
Follow me for updates
When I'm not coding...
Me and Tina are on a motorcycle adventure around Australia.
Come along for the ride!