DEV Community

Ryohlan
Ryohlan

Posted on

How to handle Next.js Api routes more type safely

Create a utility method.

// handleMethods.ts type ApiResponse<Data = {}, Error = { message: string }> = | { result: true; data: Data } | { result: false; data: Error } export const handleMethods = <Q extends Record<string, string> = {}>() => { const handlers: { [key: string]: NextApiHandler | undefined } = {} const methodHandler = { get: <T, E = string>( handler: ( req: NextApiRequest & { query: Q }, res: NextApiResponse<ApiResponse<T, E>>, ) => void | Promise<void>, ) => { handlers['GET'] = handler return methodHandler }, post: <T, E = string>( handler: ( req: NextApiRequest & { query: Q }, res: NextApiResponse<ApiResponse<T, E>>, ) => void | Promise<void>, ) => { handlers['POST'] = handler return methodHandler }, put: <T, E = string>( handler: ( req: NextApiRequest & { query: Q }, res: NextApiResponse<ApiResponse<T, E>>, ) => void | Promise<void>, ) => { handlers['PUT'] = handler return methodHandler }, delete: <T, E = string>( handler: ( req: NextApiRequest & { query: Q }, res: NextApiResponse<ApiResponse<T, E>>, ) => void | Promise<void>, ) => { handlers['DELETE'] = handler return methodHandler }, prepare: (): NextApiHandler<ApiResponse> => (req, res) => { if (handlers[req.method]) { return handlers[req.method](req, res) } else { return res.status(404).json({ result: false, data: { message: 'not found' } }) } }, } return methodHandler } 
Enter fullscreen mode Exit fullscreen mode

Usage

// pages/api/users/index.ts type User = {...} export default handleMethods() .get<Array<User>>(async (req, res) => { try { const result = await findUsers() return res.json({ result: true, data: result }) } catch(e) { return res.status(400).json({ result: false, data: e }) } }) .post<User>(async (req, res) => { try { const result = await createUser() return res.json({ result: true, data: result }) } catch(e) { return res.status(400).json({ result: false, data: e }) } }) .prepare() 
Enter fullscreen mode Exit fullscreen mode
// pages/api/users/[id].ts export default handleMethods({ id: string }) .get<Array<User>>(async (req, res) => { try { // you can access query.id type safely. const result = await findUsers({ id: req.query.id }) return res.json({ result: true, data: result }) } catch(e) { return res.status(400).json({ result: false, data: e }) } }) .prepare() 
Enter fullscreen mode Exit fullscreen mode

Top comments (4)

Collapse
 
hassnainabass profile image
Hassnain Abass

This is good,
But don't you think you have made very simple thing into a very complicated one?

Collapse
 
ryohlan profile image
Ryohlan

Next.js NextApiHandler can define only one response body type.
So if a route has multiple methods and response body types, you will use the Type Assertion and you will need to write many codes more.
This utility method can decrease codes a little.

Collapse
 
mateo_garcia profile image
Mateo Garcia

Nice article, btw you have a little typo in your Array types :)

Collapse
 
ryohlan profile image
Ryohlan

I missed it. Thank you ;)