two dtos for one controller in NestJS - javascript

I'm trying to get different types of dtos from body. If firstDto is incoming get it and transform, if secondDto is incoming transform it to secondDto. But it's not working and I have no idea. Here is my code;
#Controller('log')
export class LogsController {
constructor(private logsService: LogsService) {}
#Post()
async createStatusLog(#Body() body: CreateMessageDto | CreateStatusDto) {
console.log(body.constructor.name);
return body;
}
}
app.module.ts
providers: [
AppService,
{
provide: APP_PIPE,
useValue: new ValidationPipe({
whitelist: true,
transform: true,
}),
},
],

Related

How to inject service inside a module function NestJS

I'm using pino-logger in my NestJS project to log the activities in my application, and I'm logging the object along with ReqId so I can trace the whole activity inside one request. I'd like to use the same "ReqId" in another place as well, but I'm unsure of how to move it outside of the module, so for that, I'm thinking to save that generated ReqId into the CacheManager but not sure how to inject CacheManager class inside genReqId function. Please look over the code below.
app.module.ts
#Module({
imports: [
LoggerModule.forRoot({
pinoHttp: {
genReqId: (req: any) => {
// I'm thinking to use CacheManager here but I'm not sure how to inject CacheManager class here
return req.headers.req_id || uuid(); // from here it generate the request ID and I want to export this ID and use in side an another class
},
base: undefined,
quietReqLogger: true,
timestamp: false,
},
}),
],
})
export class AppModule {}
you need To create sharable service and import it Imports
#Injectable()
export class RequestIdService {
private reqId: string;
setRequestId(reqId: string) {
this.reqId = reqId;
}
getRequestId() {
return this.reqId;
}
}
than import it to logger module
imports: [
LoggerModule.forRoot({
pinoHttp: {
genReqId: (req: any) => {
this.requestIdService.setRequestId(req.headers.req_id || uuid());
return this.requestIdService.getRequestId();
},
base: undefined,
quietReqLogger: true,
timestamp: false,
},
}),
],
providers: [RequestIdService],
```
use that service by
import { RequestIdService } from './request-id.service';
this.requestIdService.getRequestId()

Can't inject service from my dynamic module into "registerAsync" of another module in NestJS

I created my own dynamic module to setup file upload for my project.
But when I try to inject service from my module in registerAsync method of MulterModule, I get this error:
Error: Nest can't resolve dependencies of the MULTER_MODULE_OPTIONS (?). Please make sure that the argument UploadsService at index [0] is available in the MulterModule context.
Potential solutions:
- If UploadsService is a provider, is it part of the current MulterModule?
- If UploadsService is exported from a separate #Module, is that module imported within MulterModule?
#Module({
imports: [ /* the Module containing UploadsService */ ]
})
UploadsModule.forRoot({
endpoint: process.env.S3_ENDPOINT,
accessKey: process.env.S3_ACCESS_KEY,
secretKey: process.env.S3_SECRET_KEY,
bucket: 'testbucket',
}),
MulterModule.registerAsync({
imports: [UploadsModule],
inject: [UploadsService],
useFactory: (uploadsService: UploadsService) => ({
storage: uploadsService.getS3MulterStorage(),
}),
}),
I followed this guide from NestJS Docs and code of my UploadsModule is:
import { DynamicModule, Module } from '#nestjs/common';
import { UploadsService } from './uploads.service';
import { UploadsModuleOptions } from './uploads.types';
#Module({})
export class UploadsModule {
static forRoot(options: UploadsModuleOptions): DynamicModule {
return {
module: UploadsModule,
providers: [
{
provide: 'UPLOADS_MODULE_OPTIONS',
useValue: options,
},
UploadsService,
],
exports: [UploadsService],
};
}
}
And this is code of my UploadsService:
#Injectable()
export class UploadsService {
private readonly client: S3Client;
private readonly bucket: string;
private readonly multerStorage: StorageEngine;
constructor(
#Inject('UPLOADS_MODULE_OPTIONS')
private readonly options: UploadsModuleOptions,
) {
this.client = new S3Client({
endpoint: options.endpoint,
credentials: {
accessKeyId: options.accessKey,
secretAccessKey: options.secretKey,
},
});
this.bucket = options.bucket;
this.multerStorage = multerS3({
s3: this.client,
bucket: this.bucket,
acl: 'private',
metadata: (req, file, cb) => {
cb(null, { fieldName: file.fieldname });
},
key: (req, file, cb) => {
cb(null, file.originalname + '-' + Date.now());
},
});
}
getS3MulterStorage() {
return this.multerStorage;
}
}
UploadsModule exports UploadsService, registerAsync method of MulterModule imports my UploadsModule and therefore must be able to inject UploadService?
Only your UploadsModule.forRoot() provides the exports of UploadsService, so when you use imports: [UploadsModule] you don't get the same providers exported. What I would suggest is create a separate module that does and import and export of the UploadsModule so you can import the wrapper and make use of module re-exporting
#Module({
imports: [UploadsModule.forRootAsync(uploadModuleOptions)],
exports: [UploadsModule]
})
export class WrapperUploadsModule {}
MulterModule.registerAsync({
imports: [WrapperUploadsModule],
inject: [UploadsService],
useFactory: (uploadsService: UploadsService) => ({
storage: uploadsService.getS3MulterStorage(),
}),
}),

GraphQLFederationModule healthcheck

I'm learning nest.js and I have an issue I may not completely understand.
In our company we have dev-gateway which checks MY_URL/.well-known/apollo/server-health endpoint to be sure services are up before creating and then download schema from MY_URL. MY_URL is variable we pass to configuration.
So I need to have GET http://MY_URL/.well-known/apollo/server-health to return { status: pass } and POST http://MY_URL/ to return schema/be graphql endpoint.
If path in GraphQLFederationModule config is equal to / it works, but if I have path defined as /graphql then:
GET http://MY_URL/.well-known/apollo/server-health returns { status: pass } and I think it's issue, I wanted graphql service under /graphql path
GET http://MY_URL/graphql/.well-known/apollo/server-health is graphql endpoint and it return error (lack of query) and I think it should return { status: pass }
GET http://MY_URL/graphql returns graphql enpoint which is OK
I prepared some minimal working version and I'm using:
"#apollo/federation": "^0.25.1",
"#nestjs/common": "^7.6.15",
"#nestjs/core": "^7.6.15",
"#nestjs/graphql": "^7.10.3",
"apollo-server-express": "^2.22.2",
"graphql": "^15.5.0",
"graphql-tools": "^7.0.4",
import { NestFactory } from '#nestjs/core';
import { Module } from '#nestjs/common';
import {
GraphQLFederationModule,
Query,
Resolver,
ResolveReference,
Directive, Field, ID, ObjectType
} from '#nestjs/graphql';
import { Controller, Get } from '#nestjs/common';
#Controller('')
export class AppController {
#Get()
healthCheck() {
return 'Hello World!';
}
}
#ObjectType()
#Directive('#key(fields: "_id")')
export class AdSpot {
#Field((type) => ID)
_id: string;
#Field((type) => String)
name: string
}
#Resolver((of) => AdSpot)
export class CatResolver {
#Query((returns) => [AdSpot], { name: 'adSpots' })
async getAdSpots() {
return [];
}
}
#Module({
providers: [CatResolver],
})
export class CatsModule {}
#Module({
imports: [
CatsModule,
GraphQLFederationModule.forRoot({
include: [CatsModule],
path: '/graphql',
autoSchemaFile: true,
sortSchema: true,
playground: true,
disableHealthCheck: false,
}),
],
controllers: [AppController],
})
export class AppModule {}
async function bootstrap() {
try {
const app = await NestFactory.create(AppModule);
await app.listen(3010);
} catch (err) {
console.log('-------------------------------------');
console.log(err);
}
}
bootstrap();
What am I doing wrong? Did I miss some configuration or is it a bug?
Use the relative path i.e
typePaths: ['**/*.graphql'],
In Your case
#Module({
imports: [
CatsModule,
GraphQLFederationModule.forRoot({
include: [CatsModule],
typePaths: ['**/*.graphql'],
autoSchemaFile: true,
sortSchema: true,
playground: true,
disableHealthCheck: false,
}),
],
controllers: [AppController],
})

override injection token in unit test

I have a simple service with an Injection token that is used to provide some kind of configuration for the service. It's all working as expected. Although I wasn't able to test all the possible scenarios in single spec file. For some reason I'm not allowed to override the Injection token defined in providers.
token.config.ts
export interface MyConfig = {
test: boolean;
}
export const ConfigToken = new InjectionToken<MyConfig>('MyConfig');
token.service.ts
#Injectable()
export class TokenRefreshService {
public autoRefreshStarted = false;
constructor(
#Inject(ConfigToken) private config: MyConfig
) {}
public initAutoRefresh(): void {
if ( this.config.test === true ) {
this.autoRefreshStarted = true;
}
}
}
token.service.spec.ts
describe('TokenRefreshService (with-auto-refresh)', () => {
let service: TokenRefreshService;
beforeEach(() => {
TestBed.configureTestingModule({
imports: [
{
provide: ConfigToken,
useValue: {}
},
TokenRefreshService
]
});
service = TestBed.get( TokenRefreshService );
});
it('should create an instance', () => {
expect( service ).toBeDefined();
});
it('should not start auto-refresh', () => {
service.initAutoRefresh();
expect( service.autoRefreshStarted ).toBeFalsy();
});
it('should start auto-refresh', () => {
TestBed.overrideProvider( ConfigToken, { useValue: { test: true } }); /// doesn't override the config token at all ///
service.initAutoRefresh();
expect( service.autoRefreshStarted ).toBeTruthy();
});
});
I would like to test scenarions when no config is provided to service or when config with an incorrect data is provided and so on. Therefore I really need a way to somehow override the injection token that is passed to the TokenService. But no matter what I tried, it just keeps returning the same data that are defined in the TestBed.configureTestingModule.
Am I doing something wrong? Or is there an easier way to do this?
Replace
imports: [
{
provide: ConfigToken,
useValue: {}
},
TokenRefreshService
]
with:
providers: [
{
provide: ConfigToken,
useValue: {}
},
TokenRefreshService
]
I guess importing a service never works

Injecting Angular 1 service into Angular 4

Using the process outlined here, I'm trying to inject Angular 1 services into an Angular 4 app. The app is bootstrapped in hybrid mode (and works as I have some Angular 4 components and services running).
Whenever I try to inject the Angular 1 service, I get Cannot read property 'get' of undefined.
upgraded-providers.ts:
import {LinksService} from "./+services/links/links";
export function linksServiceFactory(i: any) {
return i.get('bfLinksService'); // <--- Errors here!
}
export const linksServiceProvider = {
provide: LinksService,
useFactory: linksServiceFactory,
deps: ['$injector']
};
My Angular 4 service which is trying to use LinksService looks like:
#Injectable()
export class EntityService {
constructor (
private http: Http,
private links: LinksService
) {
}
get(id: string): Observable<OrgDetails> {
// Do something
}
}
And finally LinksService (the Angular 1 service, written in Typescript) looks like:
export class LinksService {
static $inject = ["$log", "$location", PROPERTIES_SERVICE];
private contentHost : string;
private thisAppHost : string;
constructor (private $log : ng.ILogService, private $location : ng.ILocationService, private props : IPropertiesService) {
this.init();
}
// Service functions elided
}
The bootstrap and module stuff:
#NgModule({
imports: [
BrowserModule,
HttpModule,
UpgradeModule,
],
declarations: [
AppComponent,
OrgSummaryComponent,
],
providers: [
EntityService,
linksServiceProvider
],
bootstrap: [
AppComponent,
],
})
export class AppModule {
ngDoBootstrap() {
// Does nothing by design.
// This is to facilitate "hybrid bootstrapping"
}
}
platformBrowserDynamic().bootstrapModule(AppModule).then(platformRef => {
const upgrade = platformRef.injector.get(UpgradeModule) as UpgradeModule;
upgrade.bootstrap(document.body, [AppModuleName], {strictDi: false});
});
The Angular 1 (legacy) stuff all works fine.
It seems like Angular cant find the $injector, but shouldn't that be there regardless?
Many thanks for any suggestions,
Jeff
Two days of my life I won't get back but...
Just found this:
https://github.com/angular/angular.io/issues/3317
Basically the documentation is wrong. By adding a constrcutor to the app module with the call to upgrade.bootstrap in it, everything works.
export class AppModule {
constructor(upgrade: UpgradeModule) {
upgrade.bootstrap(document.body, [AppModuleName], {strictDi: true});
}
// Does nothing by design.
// This is to facilitate "hybrid bootstrapping"
ngDoBootstrap() {}
}
platformBrowserDynamic().bootstrapModule(AppModule);
Thank you to those who responded.
Actually the better way to instantiate AngularJS is after:
platformBrowserDynamic().bootstrapModule(AppModule)
.then(platformRef => {
const upgrade = platformRef.injector.get(UpgradeModule) as UpgradeModule;
upgrade.bootstrap(document.body, ['app'], { strictDi: false });
})
.catch(err => console.log(err));

Categories