Steve Kinney

Full-Stack TypeScript

Adding Client-Side Schema Validation

Now that we have everything set up in our Express application, can we do something similar in our React application.

import {
	type NewTask,
	NewTaskSchema,
	type Task,
	TaskSchema,
	TasksSchema,
	type UpdateTask,
	UpdateTaskSchema,
} from 'busy-bee-schema';

const API_URL = 'http://localhost:4001';

Validating Our Tasks

Let’s make sure our response is what we think it is.

export const fetchTasks = async (showCompleted: boolean): Promise<Task[]> => {
	const url = new URL(`/tasks`, API_URL);

	if (showCompleted) {
		url.searchParams.set('completed', 'true');
	}

	const response = await fetch(url);

	if (!response.ok) {
		throw new Error('Failed to fetch tasks');
	}

	return TasksSchema.parse(await response.json());
};

Get Task

We’ll validate an individual task.

export const getTask = async (id: string): Promise<Task> => {
	const url = new URL(`/tasks/${id}`, API_URL);
	const response = await fetch(url);

	if (!response.ok) {
		throw new Error('Failed to fetch task');
	}

	return TaskSchema.parse(await response.json());
};

Create Task

For our POST requests, we’re going to want to validate the body.

export const createTask = async (task: NewTask): Promise<void> => {
	const url = new URL('/tasks', API_URL);

	const body = JSON.stringify(NewTaskSchema.parse(task));

	const response = await fetch(url, {
		method: 'POST',
		headers: {
			'Content-Type': 'application/json',
		},
		body,
	});

	if (!response.ok) {
		throw new Error('Failed to create task');
	}
};

Let’s validate the body for updating the task as well.

export const updateTask = async (id: string, task: UpdateTask): Promise<void> => {
	const url = new URL(`/tasks/${id}`, API_URL);

	const body = JSON.stringify(UpdateTaskSchema.parse(task));

	const response = await fetch(url, {
		method: 'PUT',
		headers: {
			'Content-Type': 'application/json',
		},
		body,
	});

	if (!response.ok) {
		throw new Error('Failed to update task');
	}
};

Delete Task

There is actually nothing to do here.

export const deleteTask = async (id: string): Promise<void> => {
	const url = new URL(`/tasks/${id}`, API_URL);

	const response = await fetch(url, {
		method: 'DELETE',
	});

	if (!response.ok) {
		throw new Error('Failed to delete task');
	}
};

Next: Validating the Data Layer

Last modified on .