Step 12
Normalizing server responses
Right now, we are sending responses to our user, but the format of the response is dynamic. We want to provide a concrete, predictable response format for our user. As an API developer, you can define what that means, but for the purposes of this tutorial, successful responses will look like this:
{
response: (data...)
}
And error responses will look like this:
{
error: 'Some error message...'
}
Notice that it is JSON format and you always know that you will either receive a "response" field if the request was successful or an "error" field if the request was unsuccessful.
Let's implement this in our project:
src/utils/index.js
const success = (res, response) => {
return res.send({
response
});
};
const failure = (res, status, error) => {
return res.status(status).send({
error
});
};
module.exports = {
success,
failure
};
Success responses will always have a 200 response code by default. Error responses, however, have different error codes depending on the error.
Let's rewrite our routes to use our "success" and "failure" functions:
src/routes/books.js
const express = require('express');
const { auth } = require('../middleware/passport');
const { success, failure } = require('../utils');
const Book = require('../models/book');
const router = express.Router();
router.get('/', async (req, res) => {
const books = await Book.findAll();
return success(res, books);
});
router.post('/', auth(), async (req, res) => {
const { title, author } = req.body;
if (!title) return failure(res, 400, 'Please provide a title');
if (!author) return failure(res, 400, 'Please provide a author');
const newBook = await Book.create({ title, author });
return success(res, newBook);
});
router.get('/:id', async (req, res) => {
const { id } = req.params;
const book = await Book.findById(id);
if (!book) return failure(res, 404, `Book with ID: ${id} does not exist!`);
return success(res, book);
});
router.put('/:id', auth(), async (req, res) => {
const { id } = req.params;
const { title, author } = req.body;
const book = await Book.findById(id);
if (!book) return failure(res, 404, `Book with ID: ${id} does not exist!`);
if (title) book.title = title;
if (author) book.author = author;
await book.save();
return success(res, book);
});
router.delete('/:id', auth(), async (req, res) => {
const { id } = req.params;
const book = await Book.findById(id);
if (!book) return failure(res, 404, `Book with ID: ${id} does not exist!`);
const removedBook = await book.destroy();
return success(res, removedBook);
});
module.exports = router;
src/routes/auth.js
const express = require('express');
const jwt = require('jsonwebtoken');
const { SECRET, EXPIRES_IN } = require('../config');
const { success, failure } = require('../utils');
const User = require('../models/user');
const router = express.Router();
router.post('/signup', async (req, res) => {
const { username, password } = req.body;
if (!username) return failure(res, 400, 'Please provide a username');
if (!password) return failure(res, 400, 'Please provide a password');
let user = await User.findOne({ where: { username } });
if (user) return failure(res, 401, `User with the username: ${username} already exists`);
user = await User.create({ username, password });
const token = jwt.sign({ id: user.id }, SECRET, { expiresIn: EXPIRES_IN });
return success(res, {
user,
token
});
});
router.post('/login', async (req, res) => {
const { username, password } = req.body;
if (!username) return failure(res, 400, 'Please provide a username');
if (!password) return failure(res, 400, 'Please provide a password');
let user = await User.scope('withPassword').findOne({ where: { username } });
if (!user) return failure(res, 401, `User with the username: ${username} does not exist`);
if (!user.comparePassword(password)) return failure(res, 401, `Incorrect password`);
const token = jwt.sign({ id: user.id }, SECRET, { expiresIn: EXPIRES_IN });
return success(res, {
user,
token
});
});
module.exports = router;
Last updated
Was this helpful?