DEV Community

Cover image for Using json-server to create a mock back-end for front-end development
Michael Ikoko
Michael Ikoko

Posted on

Using json-server to create a mock back-end for front-end development

Front-end development entails retrieving data from external sources, such as an API, and displaying it on the user interface.
During development, front-end developers are often in a situation where they need to interact with a back-end API, but it is not fully implemented, or readily available. In a situation like this, a mock back-end is extremely usefully.

In this article, we'll consider a tool used for creating mock API called 'JSON server'. We'll build a phonebook application, where we'll perform basic crud operations on the mock server, such as:

  • Creating new contacts.
  • Fetching contacts from the phonebook.
  • Deleting contacts.

Prerequisites

  • Knowledge of JavaScript and React.
  • Knowledge of basic operations done using a terminal
  • A web browser.
  • Node.JS installed on your computer.
  • A text editor (preferably VS Code).

A brief overview of json-server

JavaScript Object Notation(JSON) is a text-based format used for structuring data (based on JavaScript object syntax) and exchanging data in web applications. JSON-Server is a tool that creates a mock back-end. It simulates a REST API, with little to no coding in less than a minute.

Why use json-server

Here are several using for using json-server:

  • Rapid Development: json-server allows you to create a mock back-end quickly. You don't need to set up a full-fledged back-end server or database, making it ideal for prototyping and development in the early stages of a project.
  • Prototyping: It's an excellent choice for rapidly prototyping applications. You can define your data structure and API endpoints without worrying about setting up a full back-end system.
  • Isolated Development: It allows you to work on different components of an application in isolation. Front-end and back-end development can progress independently, provided they adhere to the defined API contract.

Create a phonebook application using json-server and React

This section shows how you can practically use json-server. In this section, you will build a phonebook application. The back-end will be mocked using json-server, and the front-end will be created using React. Here's a look at what we'll be building.

Phonebook Application

Create React application

Firstly, create a React application. You can bootstrap a React application using either Vite or Create React App. To create a React application using Vite and Node Package Manager(NPM):

  1. Type the following command into a terminal npm create vite@latest.
  2. Choose an appropriate project name, e.g phonebook.
  3. Choose React as the framework.
  4. Select JavaScript as the variant.

As shown on the terminal after successfully bootstraping using Vite:

  1. Navigate to the project folder, using cd phonebook.
  2. Install the dependencies using the command npm install

This tutorial will be using React-Bootstrap, and Bootstrap for styling. Although, you can use any styling framework of your preference. To install React-Bootstrap, type the following command into the terminal: npm install react-bootstrap bootstrap

Axios will be used for handling HTTP requests to the server. Install axios using the following command: npm install axios

Setting up json-server

You can install json-server globally using the following command: npm install -g json-server. A global installation is not needed, so to install json-server as a development dependency, use the following command npm install --save-dev json-server

At the root of the project directory, create a db.json file with the following data:

{
  "contacts": [
    {
      "name": "Emma Smith",
      "number": "555-123-4567",
      "id": 1
    },
    {
      "name": "Liam Johnson",
      "number": "555-234-5678",
      "id": 2
    },
    {
      "name": "Olivia Brown",
      "number": "555-345-6789",
      "id": 3
    },
    {
      "name": "Noah Davis",
      "number": "555-456-7890",
      "id": 4
    },
    {
      "name": "Ava Wilson",
      "number": "555-567-8901",
      "id": 5
    }
  ]
}
Enter fullscreen mode Exit fullscreen mode

Start the server using the following command: json-server --watch db.json. By default, the server runs on port 3000.
We can define an alternate port using the --port flag : json-server --port 3001 --watch db.json
The --watch flag looks for a file changes made to the db.json file.

Save the command for starting the server as a script in package.json:

{
    //...
  "scripts": {
    "dev": "vite",
    "build": "vite build",
    "lint": "eslint . --ext js,jsx --report-unused-disable-directives --max-warnings 0",
    "preview": "vite preview",
    "server": "json-server -p3001 --watch db.json"
  },
 }
Enter fullscreen mode Exit fullscreen mode

You can now start the server at the root of the project directory with the following command: npm run server
If you navigate to the address http://localhost:3001/contacts
on the browser, you will see the data written to the db.json file displayed in JSON format.

Server data on browser

JSON Server automatically saves changes made by PUT, POST, PATCH, or DELETE request to the db.json file.

JSON Server defines routes based on the data provided in the db.json file. Here are the default routes, based on the contents of the phonebook application:

  • GET /contacts
  • GET /contacts/1
  • POST /contacts
  • PUT /contacts/1
  • PATCH /contacts/1
  • DELETE /posts/1

Building the application

Remove the default App.css and the index.css files created by Vite from the src directory.

Create services folder

Extract the code for interacting with the external API into a separate folder called services. This helps in cleaning up the code. In the src directory, create the services folder, add a file there called contact.js, with the following content:

import axios from 'axios'
const baseUrl = 'http://localhost:3001/contacts'

const getAll = async () => {
  const response = await axios.get(baseUrl)
  return response.data
}

const addContact = async (contact) => {
  const response = await axios.post(baseUrl, contact)
  return response.data
}

const deleteContact = async (id) => {
  const response = await axios.delete(`${baseUrl}/${id}`)
  return response.data
}

export default { getAll, addContact, deleteContact }
Enter fullscreen mode Exit fullscreen mode

Manage state using useReducer

This tutorial makes use of React's inbuilt useReducer hook, and the Context API to access and manage the state of the application globally.
To do this:

  1. Create a file at the root of the project directory called ContactsContext.jsx.
  2. In the ContactsContext.js, create the reducer function that will be responsible for handling state changes.
  3. Create the context ContactsContext using React's createContext hook.
  4. Create and export the context provider, ContactsContext.Provider.
import { createContext, useReducer } from 'react'

const contactsReducer = (state, action) => {
  switch (action.type) {
  case 'SET_CONTACTS':
    return action.payload
  case 'ADD_CONTACT':
    return [...state, action.payload]
  case 'REMOVE_CONTACT':
    return state.filter(contact => contact.id !== action.payload.id)
  default:
    return state
  }
}

const ContactsContext = createContext()

export const ContactsContextProvider = (props) => {
  const [contacts, contactsDispatch] = useReducer(contactsReducer, [])

  return (
    <ContactsContext.Provider value={[contacts, contactsDispatch]}>
      {props.children}
    </ContactsContext.Provider>
  )
}

export default ContactsContext
Enter fullscreen mode Exit fullscreen mode

In main.jsx file, wrap the Contacts.Context.Provider around the App component.

import React from 'react'
import ReactDOM from 'react-dom/client'
import App from './App.jsx'
import { ContactsContextProvider } from './ContactsContext.jsx'

import 'bootstrap/dist/css/bootstrap.min.css'

ReactDOM.createRoot(document.getElementById('root')).render(
  <React.StrictMode>
    <ContactsContextProvider>
      <App />
    </ContactsContextProvider>
  </React.StrictMode>
)
Enter fullscreen mode Exit fullscreen mode

Fetching and rendering contacts, and deleting contacts

An important part of this application is fetching contacts from the server, and displaying them on the browser. To do this, create a folder in the src directory called components. In the components folder create the component 'Contacts.jsx:

import { useContext } from 'react'
import contactServices from '../services/contacts'
import ContactsContext from '../ContactsContext'

const Contacts = () => {
  const [contacts, dispatch] = useContext(ContactsContext)

  const deleteContact = async (id) => {
    const response = await contactServices.deleteContact(id)
    dispatch({
      type: 'REMOVE_CONTACT',
      payload: { id },
    })
  }

  return (
    <div className="my-4">
      <table className="table table-striped table-bordered">
        <thead>
          <tr>
            <th scope="col">Contact Name</th>
            <th scope="col">Contact Number</th>
            <th scope="col"></th>
          </tr>
        </thead>
        <tbody className="table-group-divider">
          {contacts.map((contact) => (
            <tr key={contact.id}>
              <td>{contact.name}</td>
              <td>{contact.number}</td>
              <td>
                <button
                  type="button"
                  className="btn btn-danger"
                  onClick={() => deleteContact(contact.id)}
                >
                  Delete
                </button>
              </td>
            </tr>
          ))}
        </tbody>
      </table>
    </div>
  )
}

export default Contacts
Enter fullscreen mode Exit fullscreen mode

Creating new contacts

Create a ContactForm.jsx file in the already created components directory. This component is responsible for creating new contacts.

import { useContext, useState } from 'react'
import { Button, Form, Modal } from 'react-bootstrap'
import contactServices from '../services/contacts'
import ContactsContext from '../ContactsContext'

const ContactForm = ({ show, handleClose }) => {
  const [contacts, dispatch] = useContext(ContactsContext)

  const [name, setName] = useState('')
  const [number, setNumber] = useState('')

  const addContact = async (event) => {
    event.preventDefault()
    const response = await contactServices.addContact({ name, number })
    dispatch({
      type: 'ADD_CONTACT',
      payload: response,
    })
    setName('')
    setNumber('')
    handleClose()
  }

  const handleNameChange = (event) => setName(event.target.value)
  const handleNumberChange = (event) => setNumber(event.target.value)

  return (
    <Modal show={show} onHide={handleClose}>
      <Modal.Header closeButton>
        <Modal.Title>Add Contact</Modal.Title>
      </Modal.Header>
      <Modal.Body>
        <Form onSubmit={addContact}>
          <Form.Group className="mb-3" controlId="formBasicName">
            <Form.Label>Name</Form.Label>
            <Form.Control
              type="text"
              placeholder="Enter Contact Name"
              value={name}
              onChange={handleNameChange}
            />
          </Form.Group>

          <Form.Group className="mb-3" controlId="formBasicNumber">
            <Form.Label>Number</Form.Label>
            <Form.Control
              type="text"
              placeholder="Enter Contact Number"
              value={number}
              onChange={handleNumberChange}
            />
          </Form.Group>

          <Button variant="primary" type="submit">
            Submit
          </Button>
        </Form>
      </Modal.Body>
    </Modal>
  )
}

export default ContactForm

Enter fullscreen mode Exit fullscreen mode

Image description

Bringing it all together

In the App.jsx component:

import { useContext, useEffect, useState } from 'react'
import contactServices from './services/contacts'
import ContactsContext from './ContactsContext'

import ContactForm from './components/ContactForm'
import Contacts from './components/Contacts'

const App = () => {
  const [contacts, dispatch] = useContext(ContactsContext)

  const [showContactForm, setShowContactForm] = useState(false)
  const handleCloseContactForm = () => setShowContactForm(false)
  const handleShowContactForm = () => setShowContactForm(true)

  useEffect(() => {
    const getContacts = async () => {
      const response = await contactServices.getAll()
      dispatch({
        type: 'SET_CONTACTS',
        payload: response,
      })
    }
    getContacts()
  }, [dispatch])


  return (
    <>
      <ContactForm
        handleClose={handleCloseContactForm}
        show={showContactForm}
      />

      <nav className="navbar bg-dark">
        <div className="container">
          <a className="navbar-brand text-light" href="#">
            PhoneBook
          </a>
        </div>
      </nav>
      <div className="container py-4">
        <button
          type="button"
          className="btn btn-primary"
          onClick={handleShowContactForm}
        >
          Add New
        </button>
        <Contacts />
      </div>
    </>
  )
}

export default App
Enter fullscreen mode Exit fullscreen mode

Conclusion

This article provided an introduction into json-server, and its importance in front-end web development.

The tutorial showed you how to use json-server to create a mock back-end by building a phonebook application.

Check out the full code used for this tutorial here

References

Top comments (0)