I am fairly new to TypeScript and I have recently run into a problem I can not solve.
I am creating a REST API with Express. I have a router, which calls a controller and inside the controller, I call a method of a service and then return the response.
This is how my router looks:
import express from 'express';
import { BidsController } from '../../controllers/bids.controller';
const router = express.Router();
const bidsController = new BidsController();
router.post('/bids', isAuthenticated, checkRoles(['user']), checkIsVerified, bidsController.createBid);
The router has some middleware, but neither one of them is using bidsController so I do believe they do not cause the error.
This is my bidsController:
import validator from '../validator';
import { BidsService } from '../services/bids.service';
class BidsController implements IBidsController {
bidsService;
constructor() {
this.bidsService = new BidsService();
}
async createBid(req: Request, res: Response, next: NextFunction): Promise<void> {
const { params, body } = req;
try {
validator.bids.create(params, body);
const { userId } = res.locals.tokenInfo;
const { value } = body;
const response = await this.bidsService.createBid(value, userId);
res.status(201).json(response);
} catch (exception) {
next(exception);
}
}
}
This is the Service:
class BidsService implements IBidsService {
public async createBid(value: number, userId: string): Promise<IBid> {
const bid = new Bid({
value,
user: userId,
})
await bid.save();
return bid;
}
}
So, when I use Postman to call the POST /bids endpoint, I get the error:
"TypeError: Cannot read property 'bidsService' of undefined"
Could you please help me solve this issue?
router.post('/bids', isAuthenticated, checkRoles(['user']),
checkIsVerified, bidsController.createBid.bind(bidsController)); // <- THE FIX
First, this is a JS runtime error, not related to TS.
The this keyword is dynamically determined when calling “method” of an object.
Usually you directly call the method on that object, like bidsController.createBid(). This would bind the this keyword inside createBid to bidsController.
However, in your case, you don’t call it directly. Instead you just pass the value of bidsController.createBid, which is a function, to the router.post as a callback, which would be called later.
The this keyword is unbound in this case, because when it’s called at a later time, it doesn’t have any information about bidsController. In order to provide that info, you use bidsController.createBid.bind(bidsController) to bind it beforehand.
The other way to bind ahead of time, is to use arrow function when declaring the class method.
class BidsController implements IBidsController {
createBid = async (req: Request, res: Response, next: NextFunction) => {
const { params, body } = req;
Related
I'm trying to create an input validation middleware using Express. My goal is to be able to pass 2 parameters to the middleware that validates client input. The problem is, after following multiple resources (including Express docs), my middleware seems to not be working.
// input validator middleware
export const validateInput = (schema: joi.ObjectSchema) => {
console.log('first console log');
return (req: Request, res: Response, next: NextFunction) => {
console.log('second console log');
const { error } = schema.validate(req.body);
if (error) {
const errors = error.details.map((err) => err.message);
next(new InvalidInput(errors));
}
next();
};
};
// middleware call
const commentSchema = joi
.object({
content: joi.string().alphanum().min(3).required(),
})
.options({ abortEarly: false });
export const validateCommentInput = () => {
validateInput(commentSchema);
};
After calling the middleware, I get to the "first console log", but never to the second, and my API just hangs there until I force stop. My solution otherwise would be to just pass req and next as parameters to a function validateInput(req, next, commentSchema);, but I'm not sure that's the proper way to do it. I also tried the async version with the same results.
Any help is greatly appreciated.
Your validateCommentInput function isn't returning the inner function.
The lack of curly braces in a lambda implies a return statement. However, using curly braces means you have to specify return.
So change this:
export const validateCommentInput = () => {
validateInput(commentSchema);
};
to this:
export const validateCommentInput = () => validateInput(commentSchema);
I am trying to get data from server via axios.post().
Decided to use POST and not GET because I want to send an array with ids to look up in the database, which might be too large to fit in GET query params.
I managed to send an array with ids in the body of the POST. This reaches my server. I can successfully find the items in the data base. The items are then returned in the response. The data shows up in Chrome devtools > Network (status 200). I also get the right stuff back when sending a request manually using Postman.
Everything seems to be working fine, but the response does not arrive in my data variable in the axios function.
I spent the day trying out the solutions to all the similar answers here. Nothing worked...
I also tried GET and sending the ids in query params instead, which gives the same error. I suspect I am doing something wrong with async/await because I am getting this "intermediate value" thingy.
Thanks in advance for the help.
CLIENT axios functions
const url = 'http://localhost:5000';
export const getStuff = Ids => {
axios.post(
`${url}/cart/stuff`,
{
Ids: Ids,
},
{
headers: {
'Content-Type': 'application/json',
},
}
);
};
CLIENT actions
import * as api from '../api';
export const getStuff = Ids => async dispatch => {
try {
// Ids is an array like ["5fnjknfdax", "5rknfdalfk"]
const { data } = await api.getStuff(Ids);
// this gives me the error in the title, data never comes through
//dispatch(-dolater-);
} catch (error) {
console.log(error);
}
};
SERVER controllers
export const getStuff = async (req, res) => {
try {
const { Ids } = req.body;
const stuff = await STUFF.find().where('_id').in(Ids);
console.log('SERVER', stuff);
// this works until here. request comes through and
// I can successfully find the stuff I want in the database
res.status(200).json(stuff); // this also works, response is being sent
} catch (error) {
res.status(404).json({ message: error });
}
};
SERVER routes
router.post('/cart/stuff', getStuff);
You have some extra curly braces here (or a missing return, depending on how you look at it). When you use a lambda (arrow function) with curly braces, you have to explicitly return a value or else it will return undefined. Change your code from this:
export const getStuff = Ids => {
axios.post(...);
};
to one of these:
// Option 1
export const getStuff = Ids => {
return axios.post(...);
};
// Option 2
export const getStuff = Ids => axios.post(...);
Either format will return the actual axios promise, instead of the default undefined.
export const fetchPost = () => {
return axios.get(url);
};
This works for me!!
I created an Express REST API using Dependency Injection with Inversify. I have a basic controller class
import { Request, Response, NextFunction } from 'express';
import { injectable, inject } from 'inversify';
import { IUserController } from './IUserController';
import { AppEntity } from '../../enterpriseBusinessRules/entities/AppEntity';
import { UserEntity } from '../../enterpriseBusinessRules/entities/UserEntity';
import { GroupEntity } from '../../enterpriseBusinessRules/entities/GroupEntity';
import { IUserUseCases } from '../../applicationBusinessRules/useCases/IUserUseCases';
import { IOCTypes } from '../../iOC/IOCTypes';
#injectable()
export class UserController implements IUserController {
public userUseCases: IUserUseCases;
constructor(#inject(IOCTypes.IUserUseCases) userUseCases: IUserUseCases) {
this.userUseCases = userUseCases;
}
public async fetchUsers(request: Request, response: Response, next: NextFunction): Promise<void>{
try {
const users: UserEntity[] = await this.userUseCases.fetchUsers(request);
response.status(200).json({
message: 'Users were fetched.',
users,
});
} catch (error) {
next(error);
}
}
}
Whenever I request the route /users the function fetchUsers gets executed. Unforunately my API crashes and throws this error
RangeError [ERR_HTTP_INVALID_STATUS_CODE]: Invalid status code:
undefined
I debugged and found this error
TypeError: Cannot read property 'userUseCases' of undefined
Within the constructor of the class this is set and works fine. Within the fetchUsers function this is undefined. How can I fix this? Because I have to use this to get access to my userUseCases variable.
Edit: Thanks to Adam Kosmalas comment I was able to fix it by binding the function within the constructor
this.fetchUsers = this.fetchUsers.bind(this)
but I don't know if this is the best solution. Then I would have to bind every function in every class within its constructor...
Any other ideas?
Another options is to use arrow functions for method declarations. This way you don't need to explicitly bind this:
public fetchUsers = async (request: Request, response: Response, next: NextFunction): Promise<void> => {
try {
const users: UserEntity[] = await this.userUseCases.fetchUsers(request);
response.status(200).json({
message: 'Users were fetched.',
users,
});
} catch (error) {
next(error);
}
}
Inside the constructor bind "this" to your function using bind(this) this will make sure It will execute the function in context of "this" of the class.
constructor(#inject(IOCTypes.IUserUseCases) userUseCases: IUserUseCases) {
this.userUseCases = userUseCases;
this.fetchUsers = this.fetchUsers.bind(this)
}
It was not working because your function was executing somewhere else thus it was getting 'this' from there
If you don't want to bind and properties are public then you can directly access if from outside of the service like.
const users: UserEntity[] = await this.nameOfTheImportedService.userUseCases.fetchUsers(request);
I have a module, for the purposes of learning testing, that looks like this:
api.js
import axios from "axios";
const BASE_URL = "https://jsonplaceholder.typicode.com/";
const URI_USERS = 'users/';
export async function makeApiCall(uri) {
try {
const response = await axios(BASE_URL + uri);
return response.data;
} catch (err) {
throw err.message;
}
}
export async function fetchUsers() {
return makeApiCall(URI_USERS);
}
export async function fetchUser(id) {
return makeApiCall(URI_USERS + id);
}
export async function fetchUserStrings(...ids) {
const users = await Promise.all(ids.map(id => fetchUser(id)));
return users.map(user => parseUser(user));
}
export function parseUser(user) {
return `${user.name}:${user.username}`;
}
Pretty straight forward stuff.
Now I want to test that fetchUserStrings method, and to do that I want to mock/spy on both fetchUser and parseUser. At the same time - I don't want the behaviour of parseUser to stay mocked - for when I'm actually testing that.
I run in the problem that it seems that it is not possible to mock/spy on functions within the same module.
Here are the resources I've read about it:
How to mock a specific module function? Jest github issue. (100+ thumbs up).
where we're told:
Supporting the above by mocking a function after requiring a module is impossible in JavaScript – there is (almost) no way to retrieve the binding that foo refers to and modify it.
The way that jest-mock works is that it runs the module code in isolation and then retrieves the metadata of a module and creates mock functions. Again, in this case it won't have any way to modify the local binding of foo.
Refer to the functions via an object
The solution he proposes is ES5 - but the modern equivalent is described in this blog post:
https://luetkemj.github.io/170421/mocking-modules-in-jest/
Where, instead of calling my functions directly, I refer to them via an object like:
api.js
async function makeApiCall(uri) {
try {
const response = await axios(BASE_URL + uri);
return response.data;
} catch (err) {
throw err.message;
}
}
async function fetchUsers() {
return lib.makeApiCall(URI_USERS);
}
async function fetchUser(id) {
return lib.makeApiCall(URI_USERS + id);
}
async function fetchUserStrings(...ids) {
const users = await Promise.all(ids.map(id => lib.fetchUser(id)));
return users.map(user => lib.parseUser(user));
}
function parseUser(user) {
return `${user.name}:${user.username}`;
}
const lib = {
makeApiCall,
fetchUsers,
fetchUser,
fetchUserStrings,
parseUser
};
export default lib;
Other posts that suggest this solution:
https://groups.google.com/forum/#!topic/sinonjs/bPZYl6jjMdg
https://stackoverflow.com/a/45288360/1068446
And this one seems to be a variant of the same idea:
https://stackoverflow.com/a/47976589/1068446
Break the object into modules
An alternative, is that I would break my module up, such that I'm never calling functions directly within each other.
eg.
api.js
import axios from "axios";
const BASE_URL = "https://jsonplaceholder.typicode.com/";
export async function makeApiCall(uri) {
try {
const response = await axios(BASE_URL + uri);
return response.data;
} catch (err) {
throw err.message;
}
}
user-api.js
import {makeApiCall} from "./api";
export async function fetchUsers() {
return makeApiCall(URI_USERS);
}
export async function fetchUser(id) {
return makeApiCall(URI_USERS + id);
}
user-service.js
import {fetchUser} from "./user-api.js";
import {parseUser} from "./user-parser.js";
export async function fetchUserStrings(...ids) {
const users = await Promise.all(ids.map(id => lib.fetchUser(id)));
return ids.map(user => lib.parseUser(user));
}
user-parser.js
export function parseUser(user) {
return `${user.name}:${user.username}`;
}
And that way I can mock the dependency modules when I'm testing the dependant module, no worries.
But I'm not sure that breaking up the modules like this is even feasible - I imagine that there might be a circumstance where you have circular dependencies.
There are some alternatives:
Dependency injection in the function:
https://stackoverflow.com/a/47804180/1068446
This one looks ugly as though, imo.
Use babel-rewire plugin
https://stackoverflow.com/a/52725067/1068446
I have to admit - I haven't looked at this much.
Split your test into multiple files
Am investigating this one now.
My question: This is all quite a frustrating and fiddly way of testing - is there a standard, nice and easy, way people are writing unit tests in 2018, that specifically solve this issue?
As you've already discovered attempting to directly test an ES6 module is extremely painful. In your situation it sounds like you are transpiling the ES6 module rather than testing it directly, which would likely generate code that looks something like this:
async function makeApiCall(uri) {
...
}
module.exports.makeApiCall = makeApiCall;
Since the other methods are calling makeApiCall directly, rather than the export, even if you tried to mock the export nothing would happen. As it stands ES6 module exports are immutable, so even if you did not transpile the module you would likely still have issues.
Attaching everything to a "lib" object is probably the easiest way to get going, but it feels like a hack, not a solution. Alternatively using a library that can rewire the module is a potential solution, but its extremely hokey and in my opinion it smells. Usually when you're running into this type of code smell you have a design problem.
Splitting up the modules into tiny pieces feels like a poor mans dependency injection, and as you've stated you'll likely run into issues quickly. Real dependency injection is probably the most robust solution, but it's something you need to build from the ground up, it's not something that you can just plug into an existing project and expect to have things working immediately.
My suggestion? Create classes and use them for testing instead, then just make the module a thin wrapper over an instance of the class. Since you're using a class you'll always be referencing your method calls using a centralized object (the this object) which will allow you to mock out the things you need. Using a class will also give you an opportunity to inject data when you construct the class, giving you extremely fine grained control in your tests.
Let's refactor your api module to use a class:
import axios from 'axios';
export class ApiClient {
constructor({baseUrl, client}) {
this.baseUrl = baseUrl;
this.client = client;
}
async makeApiCall(uri) {
try {
const response = await this.client(`${this.baseUrl}${uri}`);
return response.data;
} catch (err) {
throw err.message;
}
}
async fetchUsers() {
return this.makeApiCall('/users');
}
async fetchUser(id) {
return this.makeApiCall(`/users/${id}`);
}
async fetchUserStrings(...ids) {
const users = await Promise.all(ids.map(id => this.fetchUser(id)));
return users.map(user => this.parseUser(user));
}
parseUser(user) {
return `${user.name}:${user.username}`;
}
}
export default new ApiClient({
url: "https://jsonplaceholder.typicode.com/",
client: axios
});
Now lets create some tests for the ApiClient class:
import {ApiClient} from './api';
describe('api tests', () => {
let api;
beforeEach(() => {
api = new ApiClient({
baseUrl: 'http://test.com',
client: jest.fn()
});
});
it('makeApiCall should use client', async () => {
const response = {data: []};
api.client.mockResolvedValue(response);
const value = await api.makeApiCall('/foo');
expect(api.client).toHaveBeenCalledWith('http://test.com/foo');
expect(value).toBe(response.data);
});
it('fetchUsers should call makeApiCall', async () => {
const value = [];
jest.spyOn(api, 'makeApiCall').mockResolvedValue(value);
const users = await api.fetchUsers();
expect(api.makeApiCall).toHaveBeenCalledWith('/users');
expect(users).toBe(value);
});
});
I should note that I have not tested if the provided code works, but hopefully the concept is clear enough.
While using NestJS to create API's I was wondering which is the best way to handle errors/exception.
I have found two different approaches :
Have individual services and validation pipes throw new Error(), have the controller catch them and then throw the appropriate kind of HttpException(BadRequestException, ForbiddenException etc..)
Have the controller simply call the service/validation pipe method responsible for handling that part of business logic, and throw the appropriate HttpException.
There are pros and cons to both approaches:
This seems the right way, however, the service can return Error for different reasons, how do I know from the controller which would be the corresponding kind of HttpException to return?
Very flexible, but having Http related stuff in services just seems wrong.
I was wondering, which one (if any) is the "nest js" way of doing it?
How do you handle this matter?
Let's assume your business logic throws an EntityNotFoundError and you want to map it to a NotFoundException.
For that, you can create an Interceptor that transforms your errors:
#Injectable()
export class NotFoundInterceptor implements NestInterceptor {
intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
// next.handle() is an Observable of the controller's result value
return next.handle()
.pipe(catchError(error => {
if (error instanceof EntityNotFoundError) {
throw new NotFoundException(error.message);
} else {
throw error;
}
}));
}
}
You can then use it by adding #UseInterceptors(NotFoundInterceptor) to your controller's class or methods; or even as a global interceptor for all routes. Of course, you can also map multiple errors in one interceptor.
Try it out in this codesandbox.
Nest Js provides an exception filter that handles error not handled in the application layer, so i have modified it to return 500, internal server error for exceptions that are not Http. Then logging the exception to the server, then you can know what's wrong and fix it.
import 'dotenv/config';
import { ArgumentsHost, Catch, ExceptionFilter, HttpException, HttpStatus, Logger } from '#nestjs/common';
#Catch()
export class HttpErrorFilter implements ExceptionFilter {
private readonly logger : Logger
constructor(){
this.logger = new Logger
}
catch(exception: Error, host: ArgumentsHost): any {
const ctx = host.switchToHttp();
const request = ctx.getRequest();
const response = ctx.getResponse();
const statusCode = exception instanceof HttpException ? exception.getStatus() : HttpStatus.INTERNAL_SERVER_ERROR
const message = exception instanceof HttpException ? exception.message || exception.message?.error: 'Internal server error'
const devErrorResponse: any = {
statusCode,
timestamp: new Date().toISOString(),
path: request.url,
method: request.method,
errorName: exception?.name,
message: exception?.message
};
const prodErrorResponse: any = {
statusCode,
message
};
this.logger.log( `request method: ${request.method} request url${request.url}`, JSON.stringify(devErrorResponse));
response.status(statusCode).json( process.env.NODE_ENV === 'development'? devErrorResponse: prodErrorResponse);
}
}
You may want to bind services not only to HTTP interface, but also for GraphQL or any other interface. So it is better to cast business-logic level exceptions from services to Http-level exceptions (BadRequestException, ForbiddenException) in controllers.
In the simpliest way it could look like
import { BadRequestException, Injectable } from '#nestjs/common';
#Injectable()
export class HttpHelperService {
async transformExceptions(action: Promise<any>): Promise<any> {
try {
return await action;
} catch (error) {
if (error.name === 'QueryFailedError') {
if (/^duplicate key value violates unique constraint/.test(error.message)) {
throw new BadRequestException(error.detail);
} else if (/violates foreign key constraint/.test(error.message)) {
throw new BadRequestException(error.detail);
} else {
throw error;
}
} else {
throw error;
}
}
}
}
and then
You could also use a factory or handler to when controller catch the exception (error or domain error) its map it to another HttpException.
#Controller('example')
export class ExampleController {
#Post('make')
async make(#Res() res, #Body() data: dataDTO): Promise<any> {
try {
//process result...
return res.status(HttpStatus.OK).json(result);
} catch (error) {
throw AppErrorHandler.createHttpException(error); //<---here is the error type mapping
};
};
};