I am trying to standardise responses in my express.js web app with TypeScript and I am not quite sure, how can I globally set, that responses should be for example this interface:
{
success: boolean,
data?: any,
error?: string,
}
Right now I am just writing:
async (req: Request, res: Response, next: NextFunction) => {
try {
registerResponse = await register(req.body.email, req.body.password);
} catch (error) {
return res.json({
success: false,
error: error.message,
});
}
return res.json({
success: true,
data: {
message: 'Account registered',
},
});
};
Is there a way of setting additional settings, or rewriting typedefs to achieve hardcoded res.json type?
You can't remove a function defined on a type, we could add an overload for the json
function with a module augmentation, but this will be of little use as if we get the properties wrong the compiler will pick the original version of the function which allows any.
A more radical approach would be to create a new type compatible with Response
but which removes the original json
method and replaces it with a typed version. We can use mapped types so we don't duplicate any of the original type:
// Helpers
type Diff<T extends string, U extends string> = ({ [P in T]: P } & { [P in U]: never } & { [x: string]: never })[T];
type Omit<T, K extends keyof T> = Pick<T, Diff<keyof T, K>>;
// Generic typed response, we omit 'json' and we add a new json method with the desired parameter type
type TypedResponse<T> = Omit<Response, 'json'> & { json(data: T): Response };
// An example of a typed response
type AppResponse = TypedResponse<{
success: boolean,
data?: any,
error?: string,
}>
app.get('/', async (req: Request, res: AppResponse, next: NextFunction) => {
try {
// ....
} catch (error) {
return res.json({
success: false,
error: error.message,
errors: "" // causses error
});
}
return res.json({
success: true,
data: {
message: 'Account registered',
},
});
}
Unfortunately there is no way to force developers to use the generic version (other than long sticks) but with code review, this may work well enough for you.
good solution that makes sure you have the correct API response in any TypeDoc documentation but it doesn't work if you use method chaining like: res.status(200).json(..) as you will still use the original typed json() function. So you also need to redeclare any of those methods you plan to use and make sure they return your new custom type, like this:
type TypedResponse<T> = Omit<express.Response, 'json' | 'status'> & { json(data: T) : TypedResponse<T> } & { status(code: number): TypedResponse <T> };
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With