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?