How to extends express request interface - javascript

I'm using express with typescript. I want to extend my express request interface for that I have done something like this:-
Middleware.ts
import { NextFunction, Request, Response } from 'express';
// eslint-disable-next-line #typescript-eslint/no-var-requires
const configureI18n = require('../helpers/i18n');
const [_, i18nObj] = configureI18n(false);
export interface CRequest extends Request {
i18nObj: any;
}
const langHeadersMiddleware = (
request: CRequest,
response: Response,
next: NextFunction
): void => {
try {
const language = request.headers['accept-language'];
i18nObj.setLocale(language ? language : 'en');
request.i18nObj = i18nObj;
next();
} catch (error) {
i18nObj.setLocale('en');
}
};
export default langHeadersMiddleware;
route.ts
getUserProfile.get(
'/:id',
async (request: CRequest, response: express.Response) => {
try {
const id = request.params.id;
response.json({
err: 0,
message: request.i18nObj.__('MESSAGES.USER_FETCH'),
data
});
} catch (error: any) {
response.json({ err: 1, message: error.message, error });
}
}
);
In this route I'm getting an error:-
No overload matches this call.
The last overload gave the following error.
Argument of type '(request: CRequest, response: express.Response) => Promise' is not assignable to parameter of type 'Application<Record<string, any>>'.
Type '(request: CRequest, response: Response<any, Record<string, any>>) => Promise' is missing the following properties from type 'Application<Record<string, any>>': init, defaultConfiguration, engine, set, and 61 more.
I went through so many blogs, article but everyone is using the same as I did.

you would have to fork and rework the whole express package, which would definitely not recommend :)
But You can:
add your i18n to the request in the middleware as you're doing and just use it with //#ts-ignore above it
add your i18n to the request body in the middleware and just use it

Related

How to return HTML from Next.js middleware?

I'm trying to return HTTP Status Code 410 (gone) alongside a custom simple HTML:
<h1>Error 410</h1>
<h2>Permanently deleted or Gone</h2>
<p>This page is not found and is gone from this server forever</p>
Is it possible? Because I can't find a method on NextResponse object.
How can I return HTML from middleware?
This is not supported anymore.
Middleware can no longer produce a response body as of v12.2+.
https://nextjs.org/docs/messages/returning-response-body-in-middleware
this is the type. there is no method to send html
type NextApiResponse<T = any> = ServerResponse<IncomingMessage> & {
send: Send<T>;
json: Send<T>;
status: (statusCode: number) => NextApiResponse<T>;
redirect(url: string): NextApiResponse<T>;
redirect(status: number, url: string): NextApiResponse<T>;
setPreviewData: (data: object | string, options?: {
maxAge?: number;
path?: string;
}) => NextApiResponse<T>;
clearPreviewData: (options?: {
path?: string;
}) => NextApiResponse<T>;
unstable_revalidate: () => void;
revalidate: (urlPath: string, opts?: {
unstable_onlyGenerated?: boolean;
}) => Promise<void>;
}
express has sendFile
app.get("/", (req, res) => {
res.sendFile(__dirname + "/index.html");
});
NextApiResponse, sendandjson`
res.json(body) - Sends a JSON response. body must be a serializable object
res.send(body) - Sends the HTTP response. body can be a string, an object or a Buffer
you can redirect the user to a URL where you display your html
While it's true that returning a response body from middleware has been disabled from version v12.2, Next.js v13 reintroduced the ability to produce a response as an experimental feature through the allowMiddlewareResponseBody flag in next.config.js.
// next.config.js
module.exports = {
experimental: {
allowMiddlewareResponseBody: true
}
}
After enabling this experimental flag, you can return a response from your middleware as follows.
import { NextResponse } from 'next/server'
export function middleware(request) {
return new NextResponse(
`
<h1>Error 410</h1>
<h2>Permanently deleted or Gone</h2>
<p>This page is not found and is gone from this server forever</p>
`,
{ status: 410, headers: { 'content-type': 'text/html' } }
)
}

How to throw error from custom class nodejs

I'm trying to create a global error handler in node js with typescript. I have created some custom classes which extends error class something like this:-
custom class
export abstract class CustomError extends Error {
abstract statusCode: number;
abstract errorMessage: string;
abstract data: unknown;
constructor() {
super();
Object.setPrototypeOf(this, CustomError.prototype);
}
abstract serializeError(): { message: string; fields?: string }[] | string;
}
Bad request error class
import { StatusCodes } from 'http-status-codes';
import { CustomError } from './custom.error';
class BadRequestException extends CustomError {
statusCode = StatusCodes.BAD_REQUEST;
errorMessage: string;
data: any;
constructor(errorMessage?: string) {
super();
// set default value
this.errorMessage = errorMessage || 'Bad Request';
Object.setPrototypeOf(this, BadRequestException.prototype);
}
serializeError() {
return this.errorMessage;
}
}
export default BadRequestException;
server.js
dotenv.config();
const app: Express = express();
app.use('/', routes);
app.use(globalErrorHandler);
export default app;
routes.ts
import express from 'express';
import UserController from '../controller/user/user-controller';
import NotFoundException from '../exception/not-found-exception';
import authorizationMiddleware from '../middleware/authorization-middleware';
const router = express.Router();
// users routes
router.post('/user/create', UserController.createUser);
router.get('/user/profile/:key', UserController.getProfile);
router.get('/user/directus', UserController.getDirectUs);
router.use('*', () => {
throw new NotFoundException();
});
export default router;
controller.ts
import { Request, Response } from 'express';
import { StatusCodes } from 'http-status-codes';
import { directus } from '../../config/directus-confgi';
import BadRequestException from '../../exception/bad-request-exception';
import { USER } from '../../interface/user-interface';
import UserService from './user.service';
class UserController {
// getting user profile
static async getDirectUs(request: Request, response: Response) {
try {
const user = await directus.items('articles').readOne(15);
response.status(StatusCodes.OK).json({ user });
} catch (error: any) {
throw new BadRequestException(error.message);
}
}
}
export default UserController;
Global Error Handler
import { StatusCodes } from 'http-status-codes';
import { Request, Response, NextFunction } from 'express';
import { CustomError } from '../exception/custom.error';
const globalErrorHandler = (err: any, req: Request, res: Response,next:NextFunction) => {
if (err instanceof CustomError) {
const error: { message: string; errors?: any } = {
message: err.errorMessage ? req.body.i18nObj.__(err.errorMessage) : '',
};
if (err.data) error.errors = err.serializeError();
return res.status(err.statusCode).json(error);
}
res.status(StatusCodes.INTERNAL_SERVER_ERROR).json({
message: err.message
? err.message
: req.body.i18nObj.__('MESSAGES.UNABLE_COMPLETE_REQUEST'),
error: { message: err.message, err },
});
};
export default globalErrorHandler;
I'm getting errors in my controller catch block and bad-request error class but after that my global error handler middleware function is not able to get this error, in a result, I'm not able to send this error as a response.
I don't know what I'm doing wrong or how can call my middleware function without throwing an actual error from the controller I want to use my custom error class to formate error and status code.
In Express v4, you need to manually call next() in async controllers in order for error handling middleware to work.
Catching Errors
For errors returned from asynchronous functions invoked by route handlers and middleware, you must pass them to the next() function, where Express will catch and process them.
class UserController {
// getting user profile
static async getDirectUs(
request: Request,
response: Response,
next: NextFunction
) {
try {
const user = await directus.items("articles").readOne(15);
response.status(StatusCodes.OK).json({ user });
} catch (error: any) {
// pass the custom error to `next()`
next(new BadRequestException(error.message));
}
}
}
From Express v5 you won't have to do this
Starting with Express 5, route handlers and middleware that return a Promise will call next(value) automatically when they reject or throw an error

Nestjs prisma global exception handler status is always 500

this is the all-exception.filter.ts:
#Catch()
export class AllExceptionsFilter implements ExceptionFilter {
constructor(private readonly httpAdapterHost: HttpAdapterHost) {}
catch(exception: HttpException, host: ArgumentsHost) {
const { httpAdapter } = this.httpAdapterHost;
const ctx = host.switchToHttp();
const response = ctx.getResponse<Response>();
const request = ctx.getRequest<Request>();
const message = exception.message;
const status =
exception instanceof HttpException
? exception.getStatus()
: HttpStatus.INTERNAL_SERVER_ERROR;
console.log();
const responseBody = {
success: false,
message,
};
httpAdapter.reply(ctx.getResponse(), responseBody, status);
}
}
and this is a service method that returning just one item:
findOne(id: number) {
return this.prisma.restaurant.findUniqueOrThrow({
where: {
id,
},
});
}
The problem is that findUniqueOrThrow will throw 404 if the item is not found. but in the global filter when I log the status, I always receive a 500 status code.
Here a full example for filter handling when Prisma.findFirstOrThrow throw not found exception:
notFound.filter.ts
import { ArgumentsHost, Catch, ExceptionFilter } from '#nestjs/common';
import { NotFoundError } from '#prisma/client/runtime';
import { Response } from 'express';
#Catch(NotFoundError)
export class NotFoundExceptionFilter implements ExceptionFilter {
public catch(exception: NotFoundError, host: ArgumentsHost) {
const ctx = host.switchToHttp();
const response = ctx.getResponse<Response>();
return response.status(404).json({ statusCode: 404, error: 'Not Found' });
}
}
main.ts
async function bootstrap() {
const app = await NestFactory.create(AppModule);
...
app.useGlobalFilters(new NotFoundExceptionFilter());
await app.listen(3000);
}
Et voila !
prisma on it's own does not throw an HttpException from Nest, which is where the getStatus() method exists. The error thrown will also fail the exception instanceof HttpException check. You should wrap the call in a try/catch and transform the error to the appropriate exception type so that Nest's filter can handle sending back to proper exception status code

Property 'authorization' does not exist on type 'Request'

Consider this code:
setContext(async (req, { headers }) => {
const token = await getToken(config.resources.gatewayApi.scopes)
const completeHeader = {
headers: {
...headers,
authorization:
token && token.accessToken ? `Bearer ${token.accessToken}` : '',
} as Express.Request,
}
console.log('accessToken: ', completeHeader.headers.authorization)
return completeHeader
})
Which generates the following TS error:
Property 'authorization' does not exist on type 'Request'.
This comes from trying to access completeHeader.headers.authorization. The property authorization is indeed not available on the Express.request interface. It's strange that TypeScript can't infere the type from the literal object, which clearly is of type string. When not defining the type as Express.Request an error is thrown about an unsafe any assignment.
Is it required to create a new TS interface just for this one field? Or are we using an incorrect type? The field authorization looks to be like a commonly used field for sending tokens.
The reason is because you're coercing completeHeader.headers into the Express.Request type. The coerced type overrides the inferred type.
What you can do, is expand that coerced type by doing the following:
as Express.Request & { authorization: string }
or you could create an entirely new type:
type AuthorizedRequest = Express.Request & { authorization: string };
...
as AuthorizedRequest
in my case, I needed to add user & I got error in headers with authorization(req.headers.authorization), me resolve was:
Case 1:
1.1. Where was error(req.headers.authorization), but before i had got similar error but with user:
import { IAuthRequest } from "./../types/user.type";
const checkAuth =
() => async (req: IAuthRequest, res: Response, next: NextFunction) => {
try {
//2. Second i got error here(main problem)
//i got if, i set <req:IRequestUser> for resolve
//problem with <req.user>: Property 'authorization'
//does not exist on type 'Headers'.
//And you need to change <req: IAuthRequest>, and
//resolve problems
if (!req.headers.authorization) throw new Error("Please log in");
const token = req.headers.authorization.split(" ")[1];
if (!process.env.SECRET_ACCESS_TOKEN)
throw new Error("Please create <SECRET_ACCESS_TOKEN> in .env file");
const { decoded, expired } = Jwt.verifyJwtToken(
token,
process.env.SECRET_ACCESS_TOKEN
);
if (expired) return res.status(401).send("Token has been expired");
//1. first error here
//before(Property 'authorization' does not exist on
//type 'Headers'.) i have got error here(Property
//'user' does not exist on type 'Request'.), if
//<req: Request>, you can try resolve this problem
//<req: IRequestUser> and after this, i got error
//with req.headers.authorization (see <2. Second i
//got error ...>, code above)
req.user = decoded;
next();
} catch (err) {
return res.status(400).send(err);
}
};
1.2. In folder named like "types", i have created file <user.type.ts>
and added:
export interface IUserData {
_id: string;
email: string;
username: string;
}
export interface IRequestUser extends Request {
user: IUserData;
}
export type IAuthRequest = IRequestUser & {
headers: { authorization: string };
};
You need just delete comments and code above will work correctly, comment only for understanding what was in code before error, and how i resolve this problems
Case 2:
after a while I found an even easier way:
import { IAuthRequest } from "./../types/user.type";
const checkAuth =
() => async (req: Request, res: Response, next: NextFunction) => {
try {
req as IAuthRequest;
//your code...
next();
} catch (err) {
return res.status(400).send(err);
}
};
i hope that maybe it will help to someone

TypeScript type annotation for res.body

I'm using typescript for my app node.js express.
I would like say the res.body is type personne.
I have tried this:
router.post('/',(req: Request, res: Response) => {
const defunt:PersoneModel = res.(<PersoneModel>body);
}
I have this model:
export type PersoneModel = mongoose.Document & {
nom: String,
prenom: String,
}
Can you help me?
Thank you.
Update:
As of #types/express#4.17.2, the Request type uses generics.
https://github.com/DefinitelyTyped/DefinitelyTyped/blob/master/types/express/index.d.ts#L107
interface Request<P extends core.Params = core.ParamsDictionary, ResBody = any, ReqBody = any, ReqQuery = core.Query> extends core.Request<P, ResBody, ReqBody, ReqQuery> { }
You could set the type of req.body to PersoneModel like this:
import { Request, Response } from 'express';
router.post('/',(req: Request<{}, {}, PersoneModel>, res: Response) => {
// req.body is now PersoneModel
}
For #types/express#4.17.1 and below
Encountered similar problem and I solved it using generics:
import { Request, Response } from 'express';
interface PersoneModel extends mongoose.Document {
nom: String,
prenom: String,
}
interface CustomRequest<T> extends Request {
body: T
}
router.post('/',(req: CustomRequest<PersoneModel>, res: Response) => {
// req.body is now PersoneModel
}
We can use as. This should be enough to imply that res.body is PersoneModel
const defunt = res.body as PersoneModel;
However more straightforward way is declaring type of the variable as a PersoneModel
const defunt: PersoneModel = res.body;
router.post('/',(req: Omit<Request,'body'> & { body: PersoneModel }, res: Response) => {
// code
}
this also will do, useful if you want to create abstration
Here is what worked for me (I am using node, express, and a postgres Pool connection):
import express, { Response, Request } from 'express';
export interface ITeamsRequest {
teams?: ITeam[];
divisions?: ITournamentDivision[];
}
export function setupTeams(app: express.Application, client: Pool) {
app.get(
'/teams',
async (req: Request, res: Response<ITeamsRequest>) => {
const teams = // get teams;
const divisions = // get divisions;
res.status(200);
return res.json({ teams, divisions });
},
);
}
The key thing is to manually import Request and Response, and using a type generic (Response<ITeamsRequest>) you can define your own ResBody type.
This was on express version 4.17.1 and #types/express 4.17.11

Categories