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.
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):
- Type the following command into a terminal
npm create vite@latest
. - Choose an appropriate project name, e.g phonebook.
- Choose React as the framework.
- Select JavaScript as the variant.
As shown on the terminal after successfully bootstraping using Vite:
- Navigate to the project folder, using
cd phonebook
. - 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
}
]
}
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"
},
}
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.
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 }
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:
- Create a file at the root of the project directory called
ContactsContext.jsx
. - In the
ContactsContext.js
, create the reducer function that will be responsible for handling state changes. - Create the context
ContactsContext
using React'screateContext
hook. - 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
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>
)
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
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
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
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
Top comments (0)