Let’s add some schema validation to our Express application. We’ll start by sketching out our schemas.
const TaskSchema = z.object({
id: z.string(),
title: z.string(),
description: z.string().optional(),
completed: z.boolean(),
});
const NewTaskSchema = TaskSchema.omit({ id: true, completed: true });
const UpdateTaskSchema = TaskSchema.partial().omit({ id: true });
Validating Our Tasks from the Database
The first step is making sure what we’re getting back from the database is valid.
// Validate the task
const result = TaskSchema.parse(task);
return res.json(result);
It’ll technically blow up—but that’s because SQLite stores our completed
boolean as either a 1
or a 0
. Luckily, we can coerce it into what we’re expecting.
const TaskSchema = z.object({
id: z.coerce.number(),
title: z.string(),
description: z.string().optional(),
completed: z.coerce.boolean(),
});
We could also choose to shorten everything like this:
const task = TaskSchema.or(z.undefined()).parse(await getTask.get([id]));
Validating All the Tasks
We can do something similar to /tasks
.
const TasksSchema = z.array(TaskSchema);
It’ll now look like this:
app.get('/tasks', async (req: Request, res: Response) => {
const { completed } = req.query;
const query = completed === 'true' ? completedTasks : incompleteTasks;
try {
const tasks = TasksSchema.parse(await query.all());
return res.json(tasks);
} catch (error) {
return handleError(req, res, error);
}
});
Adding Schema Validation to the Request Body
We can use NewTaskSchema
here.
app.post('/tasks', async (req: Request, res: Response) => {
try {
const task = NewTaskSchema.parse(req.body);
await createTask.run([task.title, task.description]);
return res.sendStatus(201);
} catch (error) {
return handleError(req, res, error);
}
});
Updating Tasks
Again, there are no suprises here.
app.put('/tasks/:id', async (req: Request, res: Response) => {
try {
const { id } = req.params;
const previous = TaskSchema.parse(await getTask.get([id]));
const updates = UpdateTaskSchema.parse(req.body);
const task = { ...previous, ...updates };
await updateTask.run([task.title, task.description, task.completed, id]);
return res.sendStatus(200);
} catch (error) {
return handleError(req, res, error);
}
});