Separate Modules for Backend Communication
Rodrigo Rojas
Posted on June 14, 2021
In this (final?) installment of using axios to make HTTP requests, we've managed to make GET, POST & PUT requests. However, our code has started to chonkify a bit. So now I'm going to cover how to separate our backend communication into its own module.
We can start by creating a new directory called services inside our src folder. Create a file named contacts.js.
// src/services/contacts.js
import axios from 'axios';
const baseUrl = 'http://localhost3001/people'
const getAll = () => {
const request = axios.get(baseUrl)
return request.then(response => response.data)
}
const create = (newObject) => {
const request = axios.post(baseUrl, newObject)
return request.then(response => response.data)
}
const update = (newObject, id) => {
const request = axios.put(`${baseUrl}/${id}`, newObject)
return request.then(response => response.data)
}
const remove = (id) => {
const request = axios.delete(`${baseUrl}/${id}`)
return request.then(response => response.data)
}
const contactService = { getAll, create, update, remove }
export default contactService
Our module returns an object with four functions that directly return the response data by the axios methods.
"Wait, where did remove come from? and why not call it 'delete'?"
Well, if you've been paying attention since the beginning, I mentioned we'd go through GET
, POST
, PUT
, and DELETE
requests. I intend to keep that promise. delete
is a reserved JavaScript word so just don't.
Let's take it step-by-step:
const getAll = () => {
const request = axios.get(baseUrl)
return request.then(response => response.data)
}
Our modified getAll
function still returns a promise, as the then
method of a promise also returns a promise. Once we define the parameter of the then
method to return response.data
, we've managed for our getAll
function to work. Once the HTTP request is successful, the promise returns the data as a response from the backend module.
We can now import our contactService
module into our React App.
import contactService from './services/contacts';
Our functions can be used directly from the imported contactService
module.
import React, { useState, useEffect } from 'react';
import contactService from './services/contacts';
const App = () => {
// ...
const hook = () => {
contactService
.getAll()
.then(initialContacts => {
setPeople(initialContacts)
})
}
useEffect(hook, [])
const addContact = (e) => {
e.preventDefault()
const nameObj = {
name: newName,
number: newNumber
}
contactService
.create(nameObj)
.then(returnedContact => {
setPeople(people.concat(returnedContact))
})
setNewName('')
setNewNumber('')
}
const handleSubmit = (e) => {
e.preventDefault()
const exists = people.find(person => person.name === newName)
if(exists) {
const person = people.find(p => p.name === newName)
const confirmChange = window.confirm(`${newName} already exists. Replace old number with a new one?`)
if(confirmChange) {
const id = person.id
const updatedPerson = {...person, number: newNumber}
contactService
.update(updatedPerson, id)
.then(returnedContact => {
setPeople(people.map(p => p.id !== id ? p : returnedContact))
})
} else {
setPeople(people)
}
} else {
addContact(e)
}
setNewName('')
setNewNumber('')
}
// ...
}
export default App
Looking cleaner and leaner already!
We're now ready to implement our deleteContact
function.
const App = () => {
// ...
const deleteContact = id => {
const person = people.find(p => p.id === id)
const confirmDelete = window.confirm(`Delete ${person.name}?`)
if(confirmDelete) {
contactService
.remove(id)
.then(() => {
setPeople(people.filter(p => p.id !== id))
})
// error handling that will just print to console for now.
.catch(error => {
console.log(error)
})
setNewName('')
setNewNumber('')
}
}
// ...
}
export default App
Pretty straightforward stuff. Once our deleteContact
function is called, we set a person
variable to match the id
passed. Then if confirmDelete
is true we run our remove
function passed from our contactService
module. Next, we chain .then()
which attaches a callback that is invoked when the promise is settled. Inside this callback, we filter out contacts that don't match the id
thus causing a re-render which updates our app accordingly. Lastly a .catch()
is chained for any error handling (for now we'll just log the error to console, I'll cover error handling in another post).
That does it for our journey through axios HTTP requests. I hope you had as much fun as I had following along! In the spirit of keeping things simple, I omitted adding other React components as well as the passing down of props. I wanted to mainly focus on the backend logic and functionality while using axios. The beauty of this is that one could easily pass down the functions we wrote as event handlers to our props.
Resources
Posted on June 14, 2021
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.