The problem
- Throwing readable custom errors is one of the most critical steps in the development of web applications. The communication between services must be clear and straight to the point.
The tool
- Express middlewares when building NodeJS applications
The Solution:
Build an express middleware to throw custom responses error statusCode and messages
Our example will utilize a simple API built with ExpressJS and Prisma as an ORM
// app.ts
import express, { Request, Response, NextFunction } from 'express'
import 'express-async-errors'
import { router } from './routes'
import cors from 'cors'
const app = express()
app.use(express.json())
app.use(cors())
app.use(router)
app.use((err: {err: Error, statusCode: number, message: string}, _req: Request, res: Response, _next: NextFunction) => {
if (err.err && err.err instanceof Error) {
return res.status(err.statusCode).json({
message: err.message,
status: 'error'
})
}
return res.status(500).json({
status: 'error',
message: 'Internal server error'
})
})
export { app }
- In addition, we can also define how our error response will be displayed to our clients. Here you can choose to show whatever you want and whatever you feel is pertinent for the client to see.
// lib/error
const builder = ({ statusCode, message }: {statusCode: number, message: string}) => {
return {
err: new Error(),
statusCode,
message
}
}
export const error = { builder }
- The object error should be thrown in the following manner
// src/services/userService.ts
import { error } from '../../lib/error'
import prismaClient from '../database/client'
import { IUser } from '../types/IUser'
interface ICreateUserRequest {
name: string
email: string
password: string
}
const create = async ({ name, email, password }: ICreateUserRequest): Promise<IUser> => {
if (!email) {
throw error.builder({ statusCode: 422, message: 'Email not provided' })
}
if (await prismaClient.user.findFirst({ where: { email } })) {
throw error.builder({ statusCode: 412, message: 'Email already taken' })
}
const user = await prismaClient.user.create({
data: { name, email, password }
})
return user
}
export const userService = {create}
Top comments (6)
That's a lot like how I do it đ
I like to create a generic restful error class instead of throwing via function. Easier for me to identify what's going on
Then you can go crazy creating semantically relevant errors... đź
I appreciate your contribution to the post, which is very useful. Both methods can be used, and they are all extremely effective
nice!!! It's so good!!!
Thank you!!!!!
This is what i was looking for, nice post
I'm glad to hear that!