Node typescript genrics for json response - node.js

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;
};

Related

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);

Intercepting in Multer Mutates Request? (NestJS)

Does multer mutates any request that has given to it? I'm currently trying to intercept the request to add this in logs.
But whenever I try to execute this code first:
const newReq = cloneDeep(request); // lodash cloneDeep
const newRes = cloneDeep(response);
const postMulterRequest: any = await new Promise((resolve, reject) => {
const multerReponse = multer().any()
multerReponse(request, newRes, err => {
if (err) reject(err)
resolve(request)
})
})
files = postMulterRequest?.files;
The #UseInterceptors(FileInterceptor('file')) becomes undefined.
I have already seen the problem, it seems like the multerReponse(request, newRes, err => { mutates the request. But I don't know what the other approach I can do to fix this. (I tried JSON Serialization, Object.assign, cloneDeep, but none of those worked)
I have tried adding newReq and newRes (cloned object) to multerResponse at first it worked. But at the second time, the thread only hangs up, and doesn't proceed to next steps. Or the multerReponse(request, newRes, err => { doesn't return anything.
The whole code looks like this and used globally (some parts of here were redacted/removed; but the main logic is still the same) :
#Injectable()
export class AuditingInterceptor implements NestInterceptor {
constructor(
#InjectModel(Auditing.name)
private readonly AuditingModel: Model<Auditing>,
) {}
async intercept(
context: ExecutionContext,
next: CallHandler,
): Promise<Observable<any>> {
const request = context.switchToHttp().getRequest();
const response = context.switchToHttp().getResponse();
const { headers, method, ip, route, query, body } = request;
let bodyParam = Object.assign({}, body),
files: any;
const newReq = cloneDeep(request); // lodash cloneDeep
const newRes = cloneDeep(response);
const postMulterRequest: any = await new Promise((resolve, reject) => {
const multerReponse = multer().any();
multerReponse(newReq, newRes, (err) => {
if (err) reject(err);
resolve(newReq);
});
});
files = postMulterRequest?.files;
return next.handle().pipe(
tap(() =>
this.AuditingModel.create({
request: {
query,
bodyParam,
files,
},
timeAccessed: new Date().toISOString(),
}),
),
);
}
}
Summary of what I need to do here is I need to intercept and log the file in our DB before it gets processed in the method/endpoint that uses #UseInterceptors(FileInterceptor('file')).
I have solve this by intercepting the request using the
#Req() req
and creating a method to handle the files that was intercepted inside the FileInterceptor decorator.
Code Example:
// create logs service first to handle your queries
createLogs(file, req){
// do what you need to do with the file, and req here
const { filename } = file;
const { ip } = req
....
}
// main service
// inject the service first
constructor(#Inject(LogsService) private logsService: LogsService)
uploadHandler(file, req){
this.logsService.createLogs(file, req)
// proceed with the next steps
....
}
// controller
#Post('upload')
#UseInterceptors(FileInterceptor('file'))
testFunction(#UploadedFile() file: Express.Multer.File,, #Req req){
return this.serviceNameHere.uploadHandler(file, req);
}

Next.js not build when using getStaticPaths and props

I'm trying to run next build when using getStaticProps and getStaticPaths method in one of my routes, but it fails every time. Firstly, it just couldn't connect to my API (which is obvious, they're created using Next.js' API routes which are not available when not running a Next.js app). I thought that maybe running a development server in the background would help. It did, but generated another problems, like these:
Error: Cannot find module for page: /reader/[id]
Error: Cannot find module for page: /
> Build error occurred
Error: Export encountered errors on following paths:
/
/reader/1
Dunno why. Here's the code of /reader/[id]:
const Reader = ({ reader }) => {
const router = useRouter();
return (
<Layout>
<pre>{JSON.stringify(reader, null, 2)}</pre>
</Layout>
);
};
export async function getStaticPaths() {
const response = await fetch("http://localhost:3000/api/readers");
const result: IReader[] = await response.json();
const paths = result.map((result) => ({
params: { id: result.id.toString() },
}));
return {
paths,
fallback: false,
};
}
export async function getStaticProps({ params }) {
const res = await fetch("http://localhost:3000/api/readers/" + params.id);
const result = await res.json();
return { props: { reader: result } };
}
export default Reader;
Nothing special. Code I literally rewritten from the docs and adapted for my site.
And here's the /api/readers/[id] handler.
export default async function handler(
req: NextApiRequest,
res: NextApiResponse
) {
const knex = getKnex();
const { id } = req.query;
switch (req.method) {
case "GET":
try {
const reader = await knex
.select("*")
.from("readers")
.where("id", id)
.first();
res.status(200).json(reader);
} catch {
res.status(500).end();
}
break;
}
}
Nothing special either. So why is it crashing every time I try to build my app? Thanks for any help in advance.
You should not fetch an internal API route from getStaticProps — instead, you can write the fetch code present in API route directly in getStaticProps.
https://nextjs.org/docs/basic-features/data-fetching#write-server-side-code-directly

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

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 => ...

How to inject dependencies into Firebase/Google Cloud Functions? (unit & integration testing)

I don't know whether my question is really related to Firebase Cloud Functions, but I came across this problem trying to test my Firebase Cloud Functions.
Let's say I have a Firebase Cloud function written in NodeJS:
function.ts
import * as functions from "firebase-functions"
const admin = require("firebase-admin")
import * as authVerifier from "../../auth/authVerifier"
export default functions.https.onRequest(async (req, res) => {
let authId
try {
authId = await authVerifier.identifyClientRequest(req)
} catch (err) {
console.error(`Unauthorized request error: ${err}`)
return res.status(401).send({
error: "Unauthorized request"
})
}
}
Usually I have an interface and can easily mock any class I want to test it.
And, for example, authVerifier looks like:
authVerifier.ts
import * as express from "express"
export async function identifyClientRequest(req: express.Request) {
return true // whatever, it doesn't really matter, should be real logic here
}
I'm trying to test function.ts and I only can pass res and req into it, e.g:
function.test.ts
it("should verify client identity", async () => {
const req = {
method: "PUT"
}
const res = { }
await myFunctions(req as express.Request, res as express.Response)
// assert that authVerifier.identifyClientRequest(req) called with passed req
})
So the question is: how can I mock authVerifier.identifyClientRequest(req) to use different implementations in function.ts and in function.test.ts?
I don't really know NodeJS/TypeScript, so I wonder if I can import another mock class of authVerifier for test or something like that.
Ok, seems like I found the answer. I'll post it here just in case.
Using sinonjs, chai we can stub our class (authVerifier in that case) to return necessary results:
const chai = require("chai")
const assert = chai.assert
const sinon = require("sinon")
import * as authVerifier from "../../../src/auth/authVerifier"
it("should verify client identity", async () => {
const req = {
method: "PUT"
}
const res = mocks.createMockResponse()
const identifyClientRequestStub = sinon.stub(authVerifier, "identifyClientRequest");
const authVerifierStub = identifyClientRequestStub.resolves("UID")
await updateUser(req as express.Request, res as express.Response)
assert.isTrue(authVerifierStub.calledWith(req))
})
And the result is:

Resources