Type '{}' is missing the following properties from type 'Request': get, header, accepts, acceptsCharsets, and 67 more - node.js

I'm testing my endpoints in offline mode mocking/faking all the data. Based on the Firebase Unit testing of Cloud functions docs, they use it as following:
const req = { query: {text: 'input'} };
const res = {
redirect: (code, url) => {
assert.equal(code, 303);
assert.equal(url, 'new_ref');
done();
}
};
// Invoke addMessage with our fake request and response objects
myFunctions.addMessage(req, res);
My code is similar:
const req = {
}
const res = {
}
updateUser(req, res)
// and this is 'updateUser()' function in another file
export default functions.https.onRequest(async (req, res) => { ... }
So I'm getting the following error:
Argument of type '{}' is not assignable to parameter of type 'Request'.
Type '{}' is missing the following properties from type 'Request': get, header, accepts, acceptsCharsets, and 67 more.
How can I avoid putting all the 67 properties? I just want to provide 'method', 'query' or 'body' properties.

Thanks #mamichels, with their help I've managed to work it out. So I'm posting the solution just in case, it may help someone. I'm using Firebase CF with Express.
import * as express from "express"
...
it("should do something", async () => {
const req = {
method: "POST"
}
const res = {
}
updateUser(req as express.Request, res as express.Response)
})
And my updateUser looks like:
export default functions.https.onRequest(async (req, res) => {
...
})

Just type it as:
(req as Request), res => ...

Related

How to mock in express with Typescript and Mocha library

How to mock 'Request' in Mocha using express with Typescript?
Current solution is following:
describe("Authorization middleware", () => {
it("Fails when no authorization header", () => {
const req = {
get: () => {
return null;
},
};
expect(isAuth(req as Request, {}, () => {}));
});
});
I have got an error Conversion of type '{ get: () => null; }' to type 'Request<ParamsDictionary, any, any, ParsedQs, Record<string, any>>' may be a mistake because neither type sufficiently overlaps with the other. If this was intentional, convert the expression to 'unknown' first.
Is forcing 'unknown' type the only solution to this problem?
You can use node-mocks-http package to create the mockups of the request and response objects for express routing functions.
E.g.
import { expect } from "chai";
import {Request, Response} from 'express';
import httpMocks from 'node-mocks-http';
const isAuth = (req: Request, res: Response) => {
// your code under test
}
describe("Authorization middleware", () => {
it("Fails when no authorization header", () => {
const req = httpMocks.createRequest();
const res = httpMocks.createResponse()
expect(isAuth(req, res));
});
});
The return value of httpMocks.createRequest() API is MockRequest type, its generic parameter is constrained by express Request type. The Request type is a subset of the MockRequest type, so it matches the Request type.

Dependecy Injection using Class into Express

I'm using Express into a TypeScript project and I have the following situation
This is my route file
...
import findAllUsersFactory from "src/factory/FindAllUsers";
routes.get("/users", findAllUsersFactory().handle);
...
This is the factory where I do a sequence of injections
const findAllUsersFactory = () => {
const findAllUserRepository = new PrismaUsersRepository();
const findAllUsersBusiness = new FindAllUsersBusiness(findAllUserRepository);
const findAllUsersController = new FindAllUsersController(findAllUsersBusiness);
return findAllUsersController;
};
This is my Controller
class FindAllUsersController {
constructor(private findUserBusiness: FindAllUsersBusiness) { }
async handle(request: Request, response: Response) {
const allUsers = await this.findUserBusiness.execute();
return response.status(200).send({ allUsers });
}
}
And finally my Business
class FindAllUsersBusiness {
constructor(private usersRepository: IUsersRepository) {}
async execute() {
return this.usersRepository.findAll();
}
}
The problem is that I'm getting an error "Cannot read property 'execute' of undefined" because the findUserBusiness into handle function is undefined. And what I can't understand is that if I change my route to
routes.get("/users", (request, response) => {
findAllUsersFactory().handle(request, response);
});
it works
I've tried to log the functions, but I can say why findUserBusiness is undefined since it came from the constructor, and since the handle functions came from an instance of FindAllUsersController it should have it "defined"
You need to make some adjustments in order to adapt your factory to the way router.get expects its parameters.
const findAllUsersFactory = (req, res) => {
const findAllUserRepository = new PrismaUsersRepository();
const findAllUsersBusiness = new FindAllUsersBusiness(findAllUserRepository);
const findAllUsersController = new FindAllUsersController(findAllUsersBusiness);
return findAllUsersController.handle(req, res)
};
Then in your router you need to do the following:
routes.get("/users", findAllUsersFactory);

Fastify Typescript request query

I'm trying to put together a simple endpoint following the Fastify with Typescript docs here:
https://www.fastify.io/docs/v3.1.x/TypeScript/
export default async function foo(fastify: any) {
const MyInstance = new Foo(fastify.db);
app.get<{ Querystring: IQueryString, Headers: IHeaders }>(
"/foo",
async (request: FastifyRequest, reply: FastifyReply) => {
console.log(request.query); // *prints query object*
const { queryObj } = request.query; // *Gives error: Object is of type 'unknown'*
const result = await MyInstance.getFoo(queryObj);
reply.status(200).send(result);
}
);
}
Why do I get the error when I try to access the request.query object and how do I fix it?
By default FastifyRequest.query's type RequestQuerystringDefault maps to unknown because one cannot guess which attributes/type you'll want to set for it.
Should you have a defined type for the query of some request, just define that request type and use it:
type MyRequest = FastifyRequest<{
Querystring: { queryObj: MyQueryObject }
}>
then specify it as the expected request type:
async (request: MyRequest, reply: FastifyReply) => {
const { queryObj } = request.query // Ok
}
If you write the code to look it like Express.js, try that one:
app.get('/foo', async (req: FastifyRequest<{
Params: {
name: string,
};
}>,
rep: FastifyReply,) => {
const name = req.params.name // string
})

Node typescript genrics for json response

Sorry it's my first time with node and ts
so I'm a little confused
export const successResponseWithData = <T extends unknown>(res, data) => {
return res.status(200).json(data) as T;
};
Usage
successResponseWithData<AuthToken>(res, token);
Is it the right way?
To use typescript in express it provides many helper methods like RequestHandler below
As per my understanding if you are trying to ensure that successResponseWithData should always be of a specific type and the response you sent back is of same type then something like this can be done:
export const interface AuthToken {
item1: <type1>
};
import {RequestHandler} from 'express';
export const apiEndPoint: RequestHandler = (req, res) => {
const resp: AuthToken = successResponseWithData();
return res.status(200).json(resp);
};
successResponseWithData: AuthToken = _ => {
// calculate token here
return token;
};

How to use value which returned from controller? Testing controllers on NestJs

Controller and method for testing:
import { Controller, Get, Response, HttpStatus, Param, Body, Post, Request, Patch, Delete, Res } from '#nestjs/common';
#Controller('api/parts')
export class PartController {
constructor(private readonly partsService: partsService) { }
#Get()
public async getParts(#Response() res: any) {
const parts = await this.partsService.findAll();
return res.status(HttpStatus.OK).json(parts);
}
}
And this is unit test which must test getParts method:
describe('PartsController', () => {
let partsController: PartsController;
let partsService: partsService;
beforeEach(async () => {
partsService = new partsService(Part);
partsController= new PartsController(partsService);
});
describe('findAll', () => {
it('should return an array of parts', async () => {
const result = [{ name: 'TestPart' }] as Part[];
jest.spyOn(partsService, 'findAll').mockImplementation(async () => result);
const response = {
json: (body?: any) => {
expect(body).toBe(result);
},
status: (code: number) => response,
};
await partsController.getParts(response);
});
});
});
This test works correctly, but I think this is a bad solution. When I investigated this problem, I saw this option:
const response = {
json: (body?: any) => {},
status: (code: number) => response,
};
expect(await partsController.getParts(response)).toBe(result);
But when I try it my test don't work, cause await partsController.getParts(response) // undefined
So what should I do to make my test look good?
In solution I use: nodeJS sequelize, nestJS, typescript
Alright so I guess your problems lies on the way you instantiate and use your controller & service.
Let NestJs Testing utils do the job for you, like this:
describe('Parts Controller', () => {
let partsController: PartsController;
let partsService: PartsService;
beforeEach(async () => {
// magic happens with the following line
const module = await Test.createTestingModule({
controllers: [
PartsController
],
providers: [
PartsService
//... any other needed import goes here
]
}).compile();
partsService = module.get<PartsService>(PartsService);
partsController = module.get<PartsController>(PartsController);
});
// The next 4 lines are optional and depends on whether you would need to perform these cleanings of the mocks or not after each tests within this describe section
afterEach(() => {
jest.restoreAllMocks();
jest.resetAllMocks();
});
it('should be defined', () => {
expect(partsController).toBeDefined();
expect(partsService).toBeDefined();
});
describe('findAll', () => {
it('should return an array of parts', async () => {
const result: Part[] = [{ name: 'TestPart' }];
jest.spyOn(partsService, 'findAll').mockImplementation(async (): Promise<Part[]> => Promise.resolve(result));
const response = {
json: (body?: any) => {},
status: (code: number) => HttpStatus.OK,
};
expect(await partsController.getParts(response)).toBe(result);
});
});
});
I haven't tested the code myself so give it a try (not too sure about the response mocking for the Response type in the Parts Controller tho).
Regarding the Parts Controller by the way you should take advantage of the Response type from express though - try to rewrite code as follows:
import { Controller, Get, Response, HttpStatus, Param, Body, Post, Request, Patch, Delete, Res } from '#nestjs/common';
import { Response } from 'express';
#Controller('api/parts')
export class PartController {
constructor(private readonly partsService: partsService) { }
#Get()
public async getParts(#Response() res: Response) { // <= see Response type from express being used here
const parts = await this.partsService.findAll();
return res.status(HttpStatus.OK).json(parts);
}
}
Finally have a look at this section of the nest official documentation, maybe it can give you some insights about what you're trying to achieve:
- Nest testing section
- Nest library approach
In the second link, at the almost beginning of the page, it is stated in the https://docs.nestjs.com/controllers#request-object section the following:
For compatibility with typings across underlying HTTP platforms (e.g., Express and Fastify), Nest provides #Res() and #Response()
decorators. #Res() is simply an alias for #Response(). Both directly
expose the underlying native platform response object interface. When
using them, you should also import the typings for the underlying
library (e.g., #types/express) to take full advantage. Note that when
you inject either #Res() or #Response() in a method handler, you put
Nest into Library-specific mode for that handler, and you become
responsible for managing the response. When doing so, you must issue
some kind of response by making a call on the response object (e.g.,
res.json(...) or res.send(...)), or the HTTP server will hang.
Hope it helps, don't hesitate to comment, or share your solution if it helped finding another one ! :)
Welcome to StackOverflow platform by the way !

Resources