One Error Shape
Part of the REST APIs section of Coddy's Node.js journey. Lesson 12 of 25.
Your API answers errors in two shapes now: a string for a 404 and zod's object for a 400. A client has to handle each one. Pick one shape for every error:
{ "error": { "code": "not_found", "message": "Invoice not found" } }
{ "error": { "code": "invalid_body", "message": "Too small: ...",
"fields": { "amount": ["Too small: expected number to be >0"] } } }code is for programs: it never changes, so a client can compare it. message is for people, and its wording may change. fields tells a form which input to mark. Write the shape in one function and call it from every route, so no route can drift:
sendError(res, 404, 'not_found', 'Invoice not found');Challenge
EasyYour API sends errors in two shapes now: a plain string for a 404, and zod's object for a 400. Give every error the same shape, so a client can read them all the same way. From now on every error looks like this, with fields added only when there is something to list:
{ "error": { "code": "not_found", "message": "Task not found" } }- In
errors.js, finishsendError(res, status, code, message, fields). It responds with the given status and the shape above. Addfieldsto the inner object only when that argument was given.sendInvalid, below it, is already written and calls your function. - In
routes/tasks.js, import both helpers:- Replace the four 404 responses (GET, PATCH, PUT and DELETE for one task) with
sendError, using codenot_foundand messageTask not found. - Replace the three 400 responses for a bad body (POST, PATCH and PUT) with
sendInvalid(res, result.error).
A bad body then looks like this (zod writes the message):
{ "error": { "code": "invalid_body", "message": "Invalid input: expected string, received number", "fields": { "title": ["Invalid input: expected string, received number"] } } } - Replace the four 404 responses (GET, PATCH, PUT and DELETE for one task) with
- In
server.js, importsendErrorand use it in the error handler at the bottom:- A body that is not valid JSON gets status 400, code
invalid_jsonand messageThe body is not valid JSON. - Anything else gets status 500, code
server_errorand messageSomething went wrong.
- A body that is not valid JSON gets status 400, code
Try it: DELETE /api/tasks/99 returns 404 with { "error": { "code": "not_found", "message": "Task not found" } }, and no fields. POST { "title": 123 } to /api/tasks: you get 400 with the invalid_body error above. The request bar refuses to send broken JSON, so the checks test the invalid_json case for you.
Try it yourself
import { z } from 'zod';
// Sends every error in one shape:
// { "error": { "code": "not_found", "message": "Task not found" } }
// with "fields" added only when it is given.
export const sendError = (res, status, code, message, fields) => {
// Send the answer here
};
// A zod error as a 400: its first problem as the message, each field's
// problems in fields.
export const sendInvalid = (res, zodError) =>
sendError(res, 400, 'invalid_body', zodError.issues[0].message, z.flattenError(zodError).fieldErrors);
This lesson includes a short quiz. Start the lesson to answer it and track your progress.
All lessons in REST APIs
1Resources and Verbs
Resources and URLsCreating with POST4Browsers, Versions and Config
What Is an OriginAllowing Another OriginCORSA Page That Calls the API2Validation and Errors
Validating InputPractice on your own: Express playground