Hi I have the following code in NestJS.I am using moduleRef and have declared a custom provider named 'blogService'.However I am getting an error that says:
'Nest can't resolve dependencies of the blogService (?). Please make
sure that the argument BlogModel at index [0] is available in the
AppModule context.'
.What exactly am I doing wrong while declaring the custom provider which is leading to this error as it seems that I am injecting the Mongoose Model as well?
app.module.ts
import { BlogService } from './blog/service/blog.service';
import { Blog } from './blog/schema/blog.schema';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { ConfigModule } from '#nestjs/config';
import { MongooseModule, getModelToken } from '#nestjs/mongoose';
#Module({
imports: [
ConfigModule.forRoot({ isGlobal: true, load: [configuration], }),
MongooseModule.forRoot(process.env.DATABASE_URL)
],
controllers: [AppController],
providers: [AppService, {
provide: 'blogService',
useFactory: () => BlogService,
inject: [getModelToken(Blog.name)]
}],
})
app.controller.ts
import { Controller, Get } from '#nestjs/common';
import { ModuleRef } from '#nestjs/core';
import { BlogService } from './blog/service/blog.service';
#Controller()
export class AppController {
constructor(private readonly modelRef: ModuleRef) { }
#Get('blogs')
async getAllBlogs(): Promise<any> {
const response = await this.modelRef.get('blogService', { strict: false }).getAllBlogs();
return response;
}
}
blog.service.ts
import { Injectable, HttpException, HttpStatus, Logger } from '#nestjs/common';
import { Blog } from '../schema/blog.schema';
import { InjectModel } from '#nestjs/mongoose';
import { Model, Types } from 'mongoose';
#Injectable()
export class BlogService {
private readonly logger = new Logger(BlogService.name);
constructor(#InjectModel(Blog.name) private blogModel: Model<Blog>) { }
async getAllBlogs() {
try {
const blogs = await this.blogModel.find().exec();
return blogs;
} catch (error) {
this.logger.error(error.message);
}
}
}
Not really sure why you're making a custom provider for this, other than possibly academic purposes. Either way, for anything you want to #InjectModel() or use getModelToken() for, you needx to have a MongooseModule.forFeature() call to register the custom provider the #nestjs/mongoose package will create for you. Once you have this you can use #InjectModel() or a custom provider like
{
provide: 'blogService',
inject: [getModelToken(Blog.name)],
useFactory: (model: Model<Blog>) => new BlogService(model)
}
I'm doing a Nest.js program but I can't find my dependencies problem. I have searched quite a lot and I have found a lot of answers regarding this problem, but I can't figure out why my code isn´t working. So I have a product module which has his DTO´s, Entity, Controller, Service and module, besides it has an interface for its service.
ProductController
import { Controller, Get } from '#nestjs/common';
import { ProductServiceInterface } from './interface/product.service.interface'
#Controller('products')
export class ProductController {
constructor(private readonly productService: ProductServiceInterface) {}
#Get()
getHello(): string {
return this.productService.test();
}
}
ProductServiceInterface
import { Injectable } from '#nestjs/common';
import {
CreateProductInput,
CreateProductOutput,
} from '../dto/create-product.dto';
import { FindProductOutput } from '../dto/find-product.dto';
export interface ProductServiceInterface {
create(input: CreateProductInput): Promise<CreateProductOutput>;
findProduct(productId: string): Promise<FindProductOutput>;
test();
}
ProductService
import { Injectable } from '#nestjs/common';
import { ProductServiceInterface } from './interface/product.service.interface';
import {
CreateProductInput,
CreateProductOutput,
} from './dto/create-product.dto';
import { Product } from './entity/product.entity';
import { InjectRepository } from '#nestjs/typeorm';
import { Repository } from 'typeorm';
import { FindProductOutput } from './dto/find-product.dto';
//import WooCommerce from '../../config/woocomerce.config';
#Injectable()
export class ProductService implements ProductServiceInterface {
constructor(
#InjectRepository(Product)
private productRepository: Repository<Product>,
) {}
public async create(
productDto: CreateProductInput,
): Promise<CreateProductOutput> {
const product = new Product();
product.name = productDto.name;
product.price = productDto.price;
product.imgUrl = productDto.imgUrl;
const savedProduct = await this.productRepository.save(product);
const productOutput = new CreateProductOutput();
productOutput.id = savedProduct.id;
return productOutput;
}
public async findProduct(idProduct: string): Promise<FindProductOutput> {
const fetchedProduct = await this.productRepository.findOne(idProduct);
const productOutput = new FindProductOutput();
productOutput.id = fetchedProduct.id;
productOutput.imgUrl = fetchedProduct.imgUrl;
productOutput.name = fetchedProduct.name;
productOutput.price = fetchedProduct.price;
return productOutput;
}
public test() {
return 'test'
// WooCommerce.get('products', {
// pero_page: 20,
// }).then((resp) => {
// return resp;
// });
}
}
ProductModule
import { ProductController } from './product.controller';
import { ProductService } from './product.service';
import { Product } from './entity/product.entity';
import { TypeOrmModule } from '#nestjs/typeorm';
import { Module } from '#nestjs/common';
#Module({
imports: [TypeOrmModule.forFeature([Product])],
controllers: [ProductController],
providers: [ProductService],
})
export class ProductModule {}
AppModule
import { Module } from '#nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { ConfigModule } from '#nestjs/config';
import configuration from 'src/config/configuration';
import { TypeOrmModule } from '#nestjs/typeorm';
import { TypeOrmConfigService } from 'src/config/typeorm.config.service';
import { ProductModule } from './modules/product/product.module';
#Module({
imports: [
ConfigModule.forRoot({
load: [configuration],
isGlobal: true,
}),
TypeOrmModule.forRootAsync({
useClass: TypeOrmConfigService,
}),
ProductModule,
],
controllers: [AppController],
providers: [AppService],
})
export class AppModule {}
I hope this code is enough for knowing where my mistake is, I don't like letting others just resolve this for me but I've been watching my code for hours and can't know how to resolve this dependencies problem.
Interfaces disappear at runtime and becomes {} or Object. Nest uses the parameter type metadata to determine what is supposed to be injected (usually via ClassType.name). You have two options to solve this
Use an abstract class instead of an interface. This makes the class still visible at runtime so ClassType.name still works.
Use #Inject('CustomToken') as the way to set the metadata for what Nest needs to inject. You then need to make sure register the custom provider using something like
{
provide: 'CustomToken',
useClass: ClassToBeUsed
}
Either of these methods should fix your issue.
I am new to Angular. I just finished developing my angular web application. When I use ng serve to serve my application during production, everything works fine. I added angular universal. Now when I run any of npm run dev:ssr or npm run build:ssr && npm run serve:ssr, my application will refuse to open, throwing NetworkError response in the console. I noticed this error occurs for the number of times http requests where sent via class 'constructors(){..}'. I have browsed through several solution but couldn't get a clue of what I'm not doing right. My backend is developed with nodejs and express. I'll appreciate any help I can get.
Here is a full example of the error response I always get in the console.
ERROR NetworkError
at XMLHttpRequest.send (C:\Users\MRBEN\Desktop\Angular\fxcore\dist\fxcore\server\main.js:200768:19)
at Observable._subscribe (C:\Users\MRBEN\Desktop\Angular\fxcore\dist\fxcore\server\main.js:19025:17)
at Observable._trySubscribe (C:\Users\MRBEN\Desktop\Angular\fxcore\dist\fxcore\server\main.js:186304:25)
at Observable.subscribe (C:\Users\MRBEN\Desktop\Angular\fxcore\dist\fxcore\server\main.js:186290:22)
at scheduleTask (C:\Users\MRBEN\Desktop\Angular\fxcore\dist\fxcore\server\main.js:105897:32)
at Observable._subscribe (C:\Users\MRBEN\Desktop\Angular\fxcore\dist\fxcore\server\main.js:105959:13)
at Observable._trySubscribe (C:\Users\MRBEN\Desktop\Angular\fxcore\dist\fxcore\server\main.js:186304:25)
at Observable.subscribe (C:\Users\MRBEN\Desktop\Angular\fxcore\dist\fxcore\server\main.js:186290:22)
at subscribeToResult (C:\Users\MRBEN\Desktop\Angular\fxcore\dist\fxcore\server\main.js:196385:23)
at MergeMapSubscriber._innerSub (C:\Users\MRBEN\Desktop\Angular\fxcore\dist\fxcore\server\main.js:191575:116)```
I was still getting this ERROR NetworkError but I found another way to make this error go away. I think this answer is relevant since I was getting the same error posted above. If this can help anyone with that same server error then that's great.
If the api request is made to the server OnInit when reloading check isPlatformBrowser first when using ng-universal example.
import { Component, OnInit, PLATFORM_ID, Inject } from '#angular/core';
import { isPlatformBrowser } from '#angular/common';
import { HttpClient, HttpHeaders } from '#angular/common/http';
export class HomeComponent implements OnInit {
public testBrowser : boolean;
public data : any;
constructor(private http: HttpClient, #Inject(PLATFORM_ID) platformId: string) {
this.testBrowser = isPlatformBrowser(platformId);
}
ngOnInit() {
if (this.testBrowser) {
//avoid server NETWORK error
this.data = this.http.get('/api');
}
}
}
I was getting this same error trying to make server calls from the client before checking isPlatformBrowser === true first OnInit and this solved my problem. Hopefully this can help this bug.
For reference this answer helped me squash this long standing bug. https://stackoverflow.com/a/46893433/4684183
I am getting the same error. Try to remove TransferHttpCacheModule from your app.module and create your own custom http transfer interceptor file.
I made a file called transfer-state.interceptor.ts and then added it to app.module providers:[] to handle this. The examples below will show how I hooked it up. I am not sure if this will definitely work for you but it did make that error go away for me.
//app.module.ts
import { BrowserModule, BrowserTransferStateModule } from '#angular/platform-browser';
import { NgModule } from '#angular/core';
import { HttpClientModule, HTTP_INTERCEPTORS } from "#angular/common/http";
//import {TransferHttpCacheModule } from '#nguniversal/common';
import { AppRoutingModule } from './app-routing/app-routing.module';
import { AppComponent } from './app.component';
import { HomeComponent } from './modules/home/home.component';
import { SliderComponent } from './components/slider/slider.component';
import { WindowRefService } from './services/window-ref.service';
//import { TransferHttpInterceptorService } from './services/transfer-http-interceptor.service';
import { TransferStateInterceptor } from './interceptors/transfer-state.interceptor';
import { ServiceWorkerModule } from '#angular/service-worker';
import { environment } from '../environments/environment';
#NgModule({
declarations: [
AppComponent,
HomeComponent,
SliderComponent
],
imports: [
BrowserModule.withServerTransition({ appId: 'serverApp' }),
BrowserTransferStateModule,
AppRoutingModule,
HttpClientModule,
ServiceWorkerModule.register('ngsw-worker.js', { enabled: environment.production })
],
providers: [
WindowRefService,
{
provide: HTTP_INTERCEPTORS,
useClass: TransferStateInterceptor,
multi: true
}
],
bootstrap: [AppComponent]
})
export class AppModule { }
This is one version of a custom transfer state file but there are a few ways to do this if this one doesn't work.
//transfer-state.interceptor.ts
import { HttpEvent, HttpHandler, HttpInterceptor, HttpRequest, HttpResponse } from '#angular/common/http';
import { Inject, Injectable, PLATFORM_ID } from '#angular/core';
import { Observable, of } from 'rxjs';
import { StateKey, TransferState, makeStateKey } from '#angular/platform-browser';
import { isPlatformBrowser, isPlatformServer } from '#angular/common';
import { tap } from 'rxjs/operators';
#Injectable()
export class TransferStateInterceptor implements HttpInterceptor {
constructor(
private transferState: TransferState,
#Inject(PLATFORM_ID) private platformId: any,
) {}
intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
// For this demo application, we will only worry about State Transfer for get requests.
if (request.method !== 'GET') {
return next.handle(request);
}
// Use the request url as the key.
const stateKey: StateKey<string> = makeStateKey<string>(request.url);
// For any http requests made on the server, store the response in State Transfer.
if (isPlatformServer(this.platformId)) {
return next.handle(request).pipe(
tap((event: HttpResponse<any>) => {
this.transferState.set(stateKey, event.body);
})
);
}
// For any http requests made in the browser, first check State Transfer for a
// response corresponding to the request url.
if (isPlatformBrowser(this.platformId)) {
const transferStateResponse = this.transferState.get<any>(stateKey, null);
if (transferStateResponse) {
const response = new HttpResponse({ body: transferStateResponse, status: 200 });
// Remove the response from state transfer, so any future requests to
// the same url go to the network (this avoids us creating an
// implicit/unintentional caching mechanism).
this.transferState.remove(stateKey);
return of(response);
} else {
return next.handle(request);
}
}
}
}
If you want to add custom cache to this you can by installing memory-cache but I haven't tried that out yet. For more references these articles helped me out a lot and maybe they can help you too.
https://itnext.io/angular-universal-caching-transferstate-96eaaa386198
https://willtaylor.blog/angular-universal-for-angular-developers/
https://bcodes.io/blog/post/angular-universal-relative-to-absolute-http-interceptor
If you haven't you may need to add ServerTransferStateModule to your app.server.module file.
//app.server.module
import { NgModule } from '#angular/core';
import {
ServerModule,
ServerTransferStateModule
} from "#angular/platform-server";
import { AppModule } from './app.module';
import { AppComponent } from './app.component';
#NgModule({
imports: [
AppModule,
ServerModule,
ServerTransferStateModule
],
bootstrap: [AppComponent],
})
export class AppServerModule {}
good luck!
I was struggling with this error for days until I found this article About how to create a relative to absolute interceptor
here's the link
https://bcodes.io/blog/post/angular-universal-relative-to-absolute-http-interceptor
I created "universal-relative.interceptor.ts" file at my src folder
put this interceptor code in "universal-relative.interceptor.ts" file
import { HttpHandler, HttpInterceptor, HttpRequest } from '#angular/common/http';
import { Inject, Injectable, Optional } from '#angular/core';
import { REQUEST } from '#nguniversal/express-engine/tokens';
import { Request } from 'express';
// case insensitive check against config and value
const startsWithAny = (arr: string[] = []) => (value = '') => {
return arr.some(test => value.toLowerCase().startsWith(test.toLowerCase()));
};
// http, https, protocol relative
const isAbsoluteURL = startsWithAny(['http', '//']);
#Injectable()
export class UniversalRelativeInterceptor implements HttpInterceptor {
constructor(#Optional() #Inject(REQUEST) protected request: Request) {}
intercept(req: HttpRequest<any>, next: HttpHandler) {
if (this.request && !isAbsoluteURL(req.url)) {
const protocolHost = `${this.request.protocol}://${this.request.get(
'host'
)}`;
const pathSeparator = !req.url.startsWith('/') ? '/' : '';
const url = protocolHost + pathSeparator + req.url;
const serverRequest = req.clone({ url });
return next.handle(serverRequest);
} else {
return next.handle(req);
}
}
}
Go to your "app.server.module.ts" file
add your interceptor like this
import { NgModule } from '#angular/core';
import {
ServerModule,
ServerTransferStateModule,
} from "#angular/platform-server";
import { AppModule } from './app.module';
import { AppComponent } from './app.component';
import { UniversalRelativeInterceptor } from 'src/universal-relative.interceptor';
import { HTTP_INTERCEPTORS } from '#angular/common/http';
#NgModule({
imports: [AppModule, ServerModule, ServerTransferStateModule],
providers: [
{
provide: HTTP_INTERCEPTORS,
useClass: UniversalRelativeInterceptor,
multi: true,
},
],
bootstrap: [AppComponent],
})
export class AppServerModule {}
And the error was GONE!
For me simply the error was that my API variable was undefined, because of the Angular SSR life-cycle. The data was only available after the browser module loaded.
I was using something like
this.isBrowser$.subscribe(isBrowser => { ... });
to set the appropriate api endpoint.
As David replied in the original issue, in my case was the resourceUrl variable that I was using, was not absolute for production environment.
environment.ts
export const environment = {
resourceUrl: 'http://localhost:8082/api/site',
siteId: '1111'
};
Like you see, for development, I was using an absolute url "http://localhost:8082/api/site" for resourceUrl environment variable. Ofcourse this was working on development mode.
environment.prod.ts
export const environment = {
resourceUrl: '/api/site',
siteId: '1111'
};
In production mode I was using a relative url (/api/site), and this was causing the issue while running "serve:ssr" which is production.
return this.http.get<ISomething>(`${environment.resourceUrl}/home/${environment.siteId}`);
So I changed environment.prod.ts to use an absolute URL. Then the issue was gone.
I am adding this reply, since maybe someone doesnt look at David comment. Thanks David.
In case someone needs, if you are using ng-universal, and because the server side rendering caused the error, then you can simply use
if (typeof window === 'object') {
// your client side httpClient code
}
I made a plain typeScript class in nest.js.
JwtTokenService.js
// JwtTokenService.js
import { Injectable, Optional } from '#nestjs/common';
import { JwtService } from '#nestjs/jwt';
import { JwtPayload } from '../modules/auth/interface/jwt-payload.interface';
#Injectable()
export class JwtTokenService {
constructor(private readonly jwtService: JwtService) {}
async generateJWT(payload: object): Promise<string> {
payload['type'] = 'access_token';
const token = this.jwtService.sign({ payload });
return token;
}
}
Now how can I use this in any controller. like user, auth and other.
Register the service in the nest application module:
import { Module } from '#nestjs/common';
import { YourController } from './path-to/your.controller';
import { JwtTokenService } from './path-to/JwtTokenService.service';
#Module({
controllers: [YourController],
providers: [JwtTokenService],
})
export class ApplicationModule {}
Then you can use it in your controller:
import { Controller, Get, Post, Body } from '#nestjs/common';
import { JwtTokenService } from './path-to/JwtTokenService.service';
#Controller('your')
export class YourController {
constructor(private readonly jwtTokenService: JwtTokenService) {}
#Get()
async get() {
// use `this.jwtTokenService`
...
}
}
Nest is using the a DependencyInjection pattern to provide the service to the controller, which is why you need to declare how the service is provided in the application module.
I would create a program (script) that launches actions when it's get run, so I'm not using routes in this program
I'm using NestJS framework (requirement).
Actually I'm trying to write my code in main.ts file and importing a service with my methods .
import { NestFactory } from '#nestjs/core';
import { AppModule } from './app.module';
import {AppService} from './app.service'
import { TreeChildren } from 'typeorm';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
await app.listen(3000);
}
let appService: AppService; <- can't use appService methods
this.appService.
bootstrap();
My service
#Injectable()
export class AppService {
constructor(
#InjectRepository(File) private readonly fileRepository: Repository<File>,
) {}
async getTypes(): Promise<File[]> {
return await this.fileRepository.find();
}
}
I would use services to treat my operations so I sould use DI, which is not working in a non class file.
I would know how to run my operations in init time in a proper way
There are two ways to do this:
A) Lifecycle Event
Use a Lifecycle Event (similar to change detection hooks in Angular) to run code and inject the services needed for it, e.g.:
Service
export class AppService implements OnModuleInit {
onModuleInit() {
console.log(`Initialization...`);
this.doStuff();
}
}
Module
export class ApplicationModule implements OnModuleInit {
constructor(private appService: AppService) {
}
onModuleInit() {
console.log(`Initialization...`);
this.appService.doStuff();
}
}
B) Execution Context
Use the Execution Context to access any service in your main.ts:
async function bootstrap() {
const app = await NestFactory.create(AppModule);
await app.listen(3000);
const appService = app.get(AppService);
}