NX workspace NestJS - loading DTO classes from libraries - nestjs

I have NestJS api in NX workspace.
I created library "model" with user.ts:
export interface User{
name: string
}
export interface UserDto extends User {
email: string
}
user.controller.ts:
import { UserDto } from "#project/model";
#Post()
async create(#Req() req, #Res() res, #Body() user: UserDto): Promise<string>{
}
I'm getting build error:
C:\app\project\node_modules#nrwl\node\src\executors\node\node-with-require-overrides.js:16
return originalLoader.apply(this, arguments);
^
Error [ERR_REQUIRE_ESM]: require() of ES Module C:\app\project\node_modules#angular\core\fesm2015\core.mjs not supported.
Instead change the require of C:\app\project\node_modules#angular\core\fesm2015\core.mjs to a dynamic import() which is available in all CommonJS modules.
But this works fine:
#Post()
async create(#Req() req, #Res() res): Promise<string>{
let user: UserDto = <UserDto>{};
}
When I created userDTO in NestJS project, it works. How to load entities or DTO from libraries to NestJS? Looks like the fault lies with the decorators.
Thanks

Related

How to exclude and include different middleware for different modules in nestjs?

I have two auth middleware in my nestjs project.
AdminAuth Middleware
UserAuth Middleware
AdminAuthMiddleware will be used in AdminModule while UserAuthMiddleware will be used in rest of the modules.
export class AppModule implements NestModule {
static register(option: DynamicModuleOptionType): DynamicModule {
return {
module: AppModule,
imports: [
BullQueueModule.register(option),
KafkaModule.register(option),
CronModule.register(option),
],
};
}
configure(consumer: MiddlewareConsumer) {
consumer.apply(CorsMiddleware).forRoutes('*');
consumer.apply(AdminAuthMiddleware).forRoutes('/v1/admin/(.*)');
consumer
.apply(UserAuthMiddleware)
.exclude(
'v1/admin/(.*)',
'/_livez',
'/_healthz',
'/_readyz',
'/swagger.json',
)
.forRoutes('*');
}
}
UserAuthMiddleware middleware is working correctly, but AdminAuthMiddleware is not registering for admin routes.
How can i solve this issue?. Any help will be highly appreciated.
I tried registering AdminAuthMiddleware in AdminModule only, it did not work.
Tried changing the sequence of middleware registration.your text

How to use NestJS configurations outside managed class

I'm trying to create some custom exceptions for my application. This mostly means i'm extending the HttpException class which is pretty simple.
However, as part of the exception message, I want to pass some of the configurations for the application.
The problem is that Exceptions are not part of the module or service. They're not managed classes, so I cannot use the ConfigService as described by the NestJS documentation.
I could use the process.env.<my_config> approach, but it seems dirty to have to use that when I'm using ConfigService everywhere in my services, specially when i'm also using .env files to load some other variables. My last alternative would be to use dotenv directly in the configuration. However all of them suffer from the same: I could be missing some important data updates/added during the app bootstrapping portion.
How can I access app level configurations from outside the managed classes?
Sample of what i'm trying to do:
import { HttpException, HttpStatus } from '#nestjs/common';
export class MyCustomException extends HttpException {
constructor(message) {
const serviceName = // Get the configuration value
const configA = // Get other configuration value
const payload = {
serviceName,
configA,
message,
}
super(payload, HttpStatus.BAD_REQUEST)
}
}
You can create an exception filter to catch your custom exception or the built-in BadRequestException, then inject ConfigService into the filter when registering the filter in main.ts.
create custom exception filter to catch your custom exception.
my-custom-exception.filter.ts
import { ExceptionFilter, Catch, ArgumentsHost, HttpStatus} from '#nestjs/common';
import { Response } from 'express';
import { ConfigService } from '#nestjs/config';
import { MyCustomException } from './my-custom.exception'; // replace this to your exception file path
#Catch(MyCustomException)
export class MyCustomExceptionFilter implements ExceptionFilter {
constructor(private readonly _configService: ConfigService) {}
catch(exception: MyCustomException, host: ArgumentsHost) {
const ctx = host.switchToHttp();
const response = ctx.getResponse<Response>();
// get configuration from .env
const serviceName = this._configService.get<string>(`SERVICE_NAME`); // replace to your key in .env
const configA = this._configService.get<string>(`CONFIG_A`); // replace to your key in .env
response
.status(HttpStatus.BAD_REQUEST)
.json({
serviceName,
configA,
message: exception.message
});
}
}
Pass configService to your custom exception filter when initializing app.
main.ts
...
const app = await NestFactory.create(AppModule);
// get configService instance, pass into filter
const configService = app.get<ConfigService>(ConfigService);
app.useGlobalFilters(new MyCustomExceptionFilter (configService));
...

NestJS and Jest: Nest can't resolve dependencies of the UserService

I am using a NestJS + MikroORM stack and trying to write tests using Jest.
On the user.service.spec.ts I am always getting the following error:
Nest can't resolve dependencies of the UserService (?). Please make sure that the argument UserRepository at index [0] is available in the RootTestModule context
The user.service.spec.ts:
describe('UserService', () => {
let userService: UserService;
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [
UserService,
{
provide: getRepositoryToken(User),
useValue: {
find: jest.fn().mockResolvedValue([]),
findOneOrFail: jest.fn().mockResolvedValue({}),
create: jest.fn().mockReturnValue({}),
save: jest.fn(),
update: jest.fn().mockResolvedValue(true),
delete: jest.fn().mockResolvedValue(true),
},
},
],
}).compile();
userService = module.get<UserService>(UserService);
});
it('should be defined with dependencies', () => {
expect(userService).toBeDefined();
});
});
The user.repository.ts:
#Repository(User)
export class UserRepository extends EntityRepository<User> {}
Why would that be happening? According to all other tutorials, it should work. Thanks.
if your UserService's constructor has
private readonly repo: UserRepository
then you should use provide: UserRepository because now your provider's token is a class references, not its name.
Nest 8 changed the way DI works, it was using string tokens before, but now it uses class references instead. The nest MikroORM adapter does register both string token and class reference for custom repositories. Here you are registering the repository yourself, so you either need to register it both ways or at least the way you use.
Importing via the type requires the class reference way. Importing via #InjectRepository() requires the string token. forFeature() call registers them both in case the entity has a custom repository class.
https://github.com/mikro-orm/nestjs/blob/e51206762f9eb3e96bfc9edbb6abbf7ae8bc08a8/src/mikro-orm.providers.ts#L82-L94
So either add the provide: UserRepository as suggested in the other answer, or use #InjectRepository() decorator.

How to get FullURL with NestJS?

How do I get the full URL of the page that NestJS is processing?
(e.g. http://localhost:3000/hoge)
//
// If you implement it with express, it looks like this.
// e.g. http://localhost:3000/hoge
//
function getFullUrl(req: express.Request) {
return `${req.protocol}://${req.get('Host')}${req.originalUrl}`;
}
You can inject the request-object using the Req() decorator allowing you to do pretty much the same thing you did in your pure express-app.
import {Controller, Get, Req} from '#nestjs/common';
import {Request} from 'express';
#Controller()
export class AppController {
#Get()
getHello(#Req() req: Request): void {
console.log(`${req.protocol}://${req.get('Host')}${req.originalUrl}`);
}
}
This of course assumes that you're using Express as your http-adapter (which is the default).

How to split Nest.js microservices into separate projects?

Let's say I want to create a simplistic cinema-management platform. It needs few microservices: movies, cinemas, payments, etc.
How would you go about doing it in Nest.js? I don't want them in the same big folder as that feels like making a monolith. I want them to be separate Nest.js projects with their own git repositories so I can orchestrate them with Kubernetes later on.
How? How to connect from service cinemas to service movies if they are two separate projects and only share, let's say, Redis?
Edit:
This is not a question about microservices in general. This is a question Nest.js specific. I read the documentation, I know there are decorators like #Client for connecting to the transport layer. I just want to know where to use that decorator and maybe see a short snippet of code on "having two separate Nest.js repositories how to connect them together so they can talk to each other".
I don't care about the transport layer, that thing I can figure out myself. I just need some advice on the framework itself as I believe the documentation is lacking.
I got it working. Basically the way to do it is to create two separate projects. Let's say - one is a createMicroservice and another is just an HTTP app (but could easily be another microservice). I used a "normal" app just so I can call it easily for testing.
Here is the main.ts file that creates microservice.
import { NestFactory } from '#nestjs/core';
import { AppModule } from './app.module';
import { Transport } from '#nestjs/common/enums/transport.enum';
async function bootstrap() {
const app = await NestFactory.createMicroservice(AppModule, {
transport: Transport.REDIS,
options: {
url: 'redis://localhost:6379',
},
});
await app.listen(() => console.log('MoviesService is running.'));
}
bootstrap();
And one of the controllers:
#Controller()
export class AppController {
constructor(private readonly appService: AppService) {}
#MessagePattern({ cmd: 'LIST_MOVIES' })
listMovies(): string[] {
return ['Pulp Fiction', 'Blade Runner', 'Hatred'];
}
}
Now - in the microservice you declare to what kinds of events should controllers react to (#MessagePattern). While in the "normal" service you do this in the controller when you want to ask other microservices for something (the main.ts is the simplest example that you get when you create a new project using #nestjs/cli.
The controller code:
#Controller()
export class AppController {
private readonly client: ClientProxy;
constructor(private readonly appService: AppService) {
this.client = ClientProxyFactory.create({
transport: Transport.REDIS,
options: {
url: 'redis://localhost:6379',
},
});
}
#Get()
listMovies() {
const pattern = { cmd: 'LIST_MOVIES' };
return this.client.send<string[]>(pattern, []);
}
}
So as long a client is connected to the same transport layer as the microservice - they can talk to each other by using the #MessagePattern.
For nicer code you can move the this.client part from a constructor to a provider and then use dependency injection by declaring the provider in the module.

Resources