What is the best practice how to organize code in Next.js api handlers? I saw this video and he puts all REST routes in two files:
pages/api/users/index.tshandles all operations that don't requireidsoGET /api/users - get all usersandPOST pages/api/users - create a new userpages/api/users/[id].tshandles all operations that requireidsoGET api/users/1 - get user by id,PUT /api/users/1 - update user, andDELETE /api/users/1 - delete a user
This means a lot of code will go into just 2 files and handled by a switch case statement. How should all this code be organized?
Every case statement should have its own try catch block for handling database calls which is a lot of repetition, I could make single try catch around entire switch but that will wrap a lot of unnecessary code, and maybe each case needs different handling? I could put single try catch in higher order function and wrap each case block with it but I'm not sure that's nice either?
Also later I will need to protect some routes with withProtected and withRole middlewares but that wont be easy because now multiple api's are handled inside single handler call.
What is the best way to organize this? Is there already good complete example existing?
// pages/api/users/index.ts
import { NextApiRequest, NextApiResponse } from 'next';
import { hash } from 'bcryptjs';
import prisma from 'lib/prisma';
/**
* POST /api/users
* Required fields in body: name, username, email, password
*/
export default async function handler(
req: NextApiRequest,
res: NextApiResponse
): Promise<void> {
const { method, body } = req;
switch (method) {
case 'GET':
// get all
// try catch again?
const users = await prisma.user.findMany();
res.status(200).json({ users });
break;
case 'POST':
// create
try {
const { name, username, email, password: _password } = body;
// todo: validate...
const _user = await prisma.user.findFirst({
where: { email },
});
if (_user)
throw new Error(`The user with email: ${email} already exists.`);
const password = await hash(_password, 10);
const user = await prisma.user.create({
data: {
name,
username,
email,
password,
},
});
res.status(201).json({ user });
} catch (error) {
res.status(500).json({ error });
}
break;
default:
res.setHeader('Allow', ['GET', 'POST']);
res.status(405).end(`Method ${method} Not Allowed`);
}
}