I want to run a middleware before Nest JS serves my React application using ServeStatic Module. I cannot get a nest middleware or even a Global middleware to run on any static routes other than '/'
main.ts
app.use([AuthRedirectMiddleware, VerifyMiddleware]);
// Or even a simple logger
app.use(function (req, res, next) {
console.log("LOG: ", req.originalUrl);
next();
});
// All 3 middlewares only run for / and /api*
// Does not run for /posts , /orders/123 (both are front end routes)
This is only working for API routes and '/'
My static serve module is setup like this:
app.module.ts
#Module({
imports: [
ConfigModule.forRoot(),
ServeStaticModule.forRoot({
rootPath: clientPath,
exclude: ["/api*"],
}),
SharedModule,
...
],
controllers: [],
providers: [],
})
I also have a globalPrefix for api routes in main.js. So all urls except for /api* go to the react application
app.setGlobalPrefix("api");
Try a middleware module:
my-middleware.module.ts
#Module({}) // you can import/provide anything you need in your module here
export class MyMiddlewareModule implements NestModule {
configure(consumer: MiddlewareConsumer) {
consumer.apply(MyMiddleware).forRoutes('*');
}
}
#Injectable()
class MyMiddleware implements NestMiddleware {
async use(req: Request, res: Response, next: (error?: any) => void) {
}
}
and then import it in your app.module.ts:
#Module({
imports: [
ConfigModule.forRoot(),
MyMiddleware, // Important for it to be before your static module
ServeStaticModule.forRoot({
rootPath: clientPath,
exclude: ["/api*"],
}),
SharedModule,
...
],
controllers: [],
providers: [],
})
Also these two links maybe useful:
https://github.com/nestjs/docs.nestjs.com/issues/1248
https://docs.nestjs.com/faq/request-lifecycle#request-lifecycle
Related
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()
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(),
}),
}),
I have a microfrontend running on port 4200, and the other one on 4201. I tried splitting up the translation files and would like to load both of the applications translations, so I wrote a custom loader that is making an http request to port 4201:
export const scope = function(http: HttpClient) {
const loader = ['en', 'de'].reduce((acc: any, lang: string) => {
acc[lang] = () => firstValueFrom(http.get(`http://localhost:4201/assets/i18n/fields/${lang}.json`));
return acc;
}, {});
return {scope: 'fields', loader: loader}
};
#NgModule({
declarations: [RootComponent],
imports: [
CommonModule,
RootRoutingModule,
FormsModule,
SharedModule.forRemote(environment),
TranslocoModule
],
providers: [
{
provide: TRANSLOCO_SCOPE,
deps: [HttpClient],
useFactory: scope
}
]
})
export class RootModule { }
Both applications are javacript frontends, hence CORS blocks it. How can I ignore cores in the frontend?
https://levelup.gitconnected.com/fixing-cors-errors-with-angular-cli-proxy-e5e0ef143f85
This article helped me. It is a workaround to enable CORS in the frontend. But be warned this is just a workaround and in my case it was neccessary because I was loading translation files from one microfrontend to the app shell.
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));
I've 3 applications which share a common framework so there's currently a bunch of duplicated code (basic ui layout, common code such as loggers etc, security/keycloak auth wrapper). I've extracted all the duplicate module code but also want to simplify the app module declaration itself. I've created an "Application" class containing a static method which takes a couple of params and builds an NgModule definition based on common modules, concatenating the passed in ones...
public static Define({modules, states, defaultState}) {
return {
imports: [
BrowserModule,
FormsModule,
HttpModule,
TreeModule,
NgbModule.forRoot(),
ToastModule.forRoot(<ToastOptions>{animate: 'flyRight', positionClass: 'toast-bottom-right'}),
UIRouterModule.forRoot(<RootModule>{ states: states.concat({name: 'app', url: '/app', component: ShellComponent}), useHash: true, otherwise: defaultState }),
CommonModule,
DialogsModule,
LayoutModule,
].concat(modules),
providers: [
{
provide: Http,
useFactory: (backend: XHRBackend, defaultOptions: RequestOptions) => new AuthHttpService(backend, defaultOptions),
deps: [XHRBackend, RequestOptions]
}
],
bootstrap: [ UIView ]
};
}
Each application can then simply call this and just list its specific modules, states and an initial/default state like this...
#NgModule(
Application.Define({
modules: [
PatientIdentityModule,
RecordViewerModule,
ResourcesModule,
],
states: [
{name: 'app.resourceList', url: '/resourceList', component: ResourcesComponent },
{name: 'app.resourceEdit', url: '/resourceEdit/:itemAction/:itemUuid', component: ResourcesComponent },
{name: 'app.patientIdentity', url: '/patientIdentity', component : PatientIdentityComponent},
{name : 'app.recordViewer', url: '/recordViewer', component : RecordViewerComponent }
],
defaultState : { state: 'app.recordViewer', params: {} }
})
)
export class AppModule {}
This works great when all the code is together but when I try extracting the Application class and building a node module library, I then get
Error: No NgModule metadata found for 'AppModule'
Any ideas or am I going about this completely the wrong way!?
Angular 2.1.0, typescript 2.0.6, webpack 1.13.2