I would like to add version for my modules, but i don't know how can I do this. I tried to create a common module.ts but the same name of services killed each us. I tried different module.ts for the versions, it was better but the services with same names didn't work.
This is my last structure:
test-module
1.0
controllers
test.controller.ts
services
test.service.ts
test.module.ts
1.1
controllers
test.controller.ts
services
test.service.ts
test.module.ts
This is my test service(s) for the versions:
import * as _ from 'lodash';
import { Injectable } from '#nestjs/common';
#Injectable()
export class TestService {
public test() {
return '1.0'; // and 1.1 in 1.1 directory
}
}
This is my module.ts:
import { Module, Logger } from '#nestjs/common';
import { TestModule as DorotTwo } from 'test-module/1.1/test.module';
import { TestModule as DorotOne } from 'test-module/1.0/test.module'
#Module({
controllers: [ProtobufController],
providers: [],
imports: [
DorotTwo,
DorotOne,
],
})
export class ProjectModule {
constructor() {
Logger.log('App initialized');
}
}
This is a simple test Controller in the project who want use the modules. A tried import TestService from 1.0 or 1.1 but the test function's response is always 1.0 because that is the last element in the import.
#Controller()
export class ProtobufController {
constructor(private readonly testService: TestService) {
console.log(this.testService.test()); // Always 1.0
}
.....
It is working if I use full different names for services for example (eg: UserAuthenticationService10, RegisterAuthenticationService10), but this is horrible and if i forget rename it in new version, it will overwrite.
Is exists an example where I can read how can I create this versioned module?
Would using custom providers be a satisfying solution for you?
Example:
// 1.0
#Module({
providers: [
{ provide: 'TestService_1.0', useClass: TestService }
]
})
export class TestModule {}
// 1.1
#Module({
providers: [
{ provide: 'TestService_1.1', useClass: TestService }
]
})
export class TestModule {}
// Then
#Controller()
export class ProtobufController {
constructor(
#Inject('TestService_1.0') private readonly testService_10,
#Inject('TestService_1.1') private readonly testService_11
) {
console.log(this.testService_10.test());
console.log(this.testService_11.test());
}
}
I obviously haven't tested this and you should adapt it to your usecase. I suggest you to have a look at https://docs.nestjs.com/fundamentals/custom-providers.
Related
I'm facing the following issue. I have a service used by a controller. The service (in the snippets below QueueService) injects a provider imported from a package. I aim to reuse the QueueService across the controller methods, but I also need to dynamically specify which provider QueueService should use.
My question is, how can I achieve this behaviour?
import { PubsubService } from '#myorg/queue'
#Module({
imports: [
ConfigModule.forRoot({
SHARED_RESOURCES_PROJECT_ID: Joi.string().required()
})
})
],
controllers: [AppController],
providers: [
{
provide: 'PUBSUB',
useValue: new PubsubService()
},
{
provide: 'INTEGRATION_PUBSUB',
useValue: new PubsubService({ projectId: process.env.SHARED_RESOURCES_PROJECT_ID })
}
]
})
export class AppModule {}
#Controller()
export class AppController {
constructor(private queueService: QueueService) {}
#Post()
async create() {
...
// here I want to use queueService with `PUBSUB` injected
return this.queueService.sendMessage(...)
}
#Patch()
async update() {
...
// here I want to use queueService with `INTEGRATION_PUBSUB` injected
return this.queueService.sendMessage(...)
}
}
#Injectable()
export class QueueService {
constructor(
// how can I dynamically change `#Inject('PUBSUB')` to `#Inject('INTEGRATION_PUBSUB')`?
#Inject('PUBSUB') private readonly pubsubService: PubsubService
) {}
async sendMessage(payload): Promise<void> {
return this.pubsubService.sendMessage(payload)
}
}
dynamic inject is not possible after object(in this case controller) created . so you have two option
1- create two QueueService (one for PUBSUB and another for INTEGRATION_PUBSUB) and inject both to controller. use those in your controller functions. (i recommend this)
2- inject both PUBSUB and INTEGRATION_PUBSUB into QueueService and pass another param in sendMessage function . so check this param to choose between PUBSUB and INTEGRATION_PUBSUB
I have a simple nestjs application, where I have set up a CacheModule using Redis store as follows:
import * as redisStore from 'cache-manager-redis-store';
CacheModule.register({
store: redisStore,
host: 'redis',
port: 6379,
}),
I would like to use it to store a single value, however, I do not want to do it the built-in way by attaching an interceptor to a controller method, but instead I want to control it manually and be able to set and retrieve the value in the code.
How would I go about doing that and would I even use cache manager for that?
You can use the official way from Nest.js:
1. Create your RedisCacheModule:
1.1. redisCache.module.ts:
import { Module, CacheModule } from '#nestjs/common';
import { ConfigModule, ConfigService } from '#nestjs/config';
import * as redisStore from 'cache-manager-redis-store';
import { RedisCacheService } from './redisCache.service';
#Module({
imports: [
CacheModule.registerAsync({
imports: [ConfigModule],
inject: [ConfigService],
useFactory: async (configService: ConfigService) => ({
store: redisStore,
host: configService.get('REDIS_HOST'),
port: configService.get('REDIS_PORT'),
ttl: configService.get('CACHE_TTL'),
}),
}),
],
providers: [RedisCacheService],
exports: [RedisCacheService] // This is IMPORTANT, you need to export RedisCacheService here so that other modules can use it
})
export class RedisCacheModule {}
1.2. redisCache.service.ts:
import { Injectable, Inject, CACHE_MANAGER } from '#nestjs/common';
import { Cache } from 'cache-manager';
#Injectable()
export class RedisCacheService {
constructor(
#Inject(CACHE_MANAGER) private readonly cache: Cache,
) {}
async get(key) {
await this.cache.get(key);
}
async set(key, value) {
await this.cache.set(key, value);
}
}
2. Inject RedisCacheModule wherever you need it:
Let's just assume we will use it in module DailyReportModule:
2.1. dailyReport.module.ts:
import { Module } from '#nestjs/common';
import { RedisCacheModule } from '../cache/redisCache.module';
import { DailyReportService } from './dailyReport.service';
#Module({
imports: [RedisCacheModule],
providers: [DailyReportService],
})
export class DailyReportModule {}
2.2. dailyReport.service.ts
We will use the redisCacheService here:
import { Injectable, Logger } from '#nestjs/common';
import { Cron } from '#nestjs/schedule';
import { RedisCacheService } from '../cache/redisCache.service';
#Injectable()
export class DailyReportService {
private readonly logger = new Logger(DailyReportService.name);
constructor(
private readonly redisCacheService: RedisCacheService, // REMEMBER TO INJECT THIS
) {}
#Cron('0 1 0 * * *') // Run cron job at 00:01:00 everyday
async handleCacheDailyReport() {
this.logger.debug('Handle cache to Redis');
}
}
You can check my sample code here.
Building on Ahmad's comment above, I used the following to enable redis in my nestjs application:
Install and setup nestjs-redis https://www.npmjs.com/package/nestjs-redis per docs.
See the docs here on how to write and read values in a Redis store:
https://github.com/NodeRedis/node-redis
If you're connection a external Redis, I recommend to use 'async-redis' package.
The code will be:
import * as redis from 'async-redis';
import redisConfig from '../../config/redis';
On redisConfig:
export default {
host: 'your Host',
port: parseInt('Your Port Conection'),
// Put the first value in hours
// Time to expire a data on redis
expire: 1 * 60 * 60,
auth_pass: 'password',
};
So, you run:
var dbConnection = redis.createClient(config.db.port, config.db.host,
{no_ready_check: true});
Now you can, execute commands like set and get for your Redis Database.
I'm trying to build an angular application to build web elements using #angular/elements module.
The root project will only be used for testing those components in a standard stand-alone app.
I created a project called "elements" which only purpose will be to create components and distribute them as web elements which works fine so far.
I am now trying to lazy load those elements. I only want the related bundles files when I'm really using the component.
Here is my app.module.ts which defines the web elements:
import { BrowserModule } from '#angular/platform-browser';
import { NgModule, Injector, DoBootstrap, CUSTOM_ELEMENTS_SCHEMA } from '#angular/core';
import { AppRoutingModule } from './app-routing.module';
import { createCustomElement } from '#angular/elements';
import { CfLabelComponent } from './cf-label/cf-label.component';
import { CfAdslComponent } from './cf-adsl/cf-adsl.component';
#NgModule({
declarations: [],
imports: [
BrowserModule,
AppRoutingModule
],
providers: [],
bootstrap: [],
})
export class AppModule implements DoBootstrap {
constructor(private injector: Injector) { }
ngDoBootstrap() {
customElements.define('cf-label', createCustomElement(CfLabelComponent, { injector: this.injector }));
customElements.define('cf-adsl', createCustomElement(CfAdslComponent, { injector: this.injector }));
}
}
I understand that Angular needs something to bootstrap on.
I don't have the classic app.component.ts as I don't need it at the end of the day.
The previous code is working but bootstraps the components cf-label & cf-adsl inside the main.js which is understandable but this is not what I want.
Could you please suggest a way to approach this matter?
I could use a little help.
Context I am making a angular 5 module for an authentication service.
How do I pass a POJO into a class as parameters?
See my answer below.
I need to use HttpClient inside this auth service
I am getting this error:
Error: Can't resolve all parameters for AuthService: ([object Object], ?).
[object Object] is angular 5 HttpClient
Can some one please explain why I am getting this error and how to resolve it?
#NgModule()
export class AuthtModule {
static forRoot(params?: iParams) {
return {
ngModule: AuthModule,
provides: [
HttpClient,
{
provide: AuthService,
useFactory: setupAuthService,
deps: [ HttpClient, params ]
}
],
imports: [ HttpClientModule ],
exports: [ AuthService ]
}
}
}
Thanks in advance.
Cheers
I figured out how to do this, as it turns out the problem was with the second parameter (hence the question mark) not the first. the reason this error occurs is because in order for parameters to be passed into a class they must first be turned into a injectable.
Here is how you do it.
First create a class model with the params
foo-params.model.ts
export class FooParams {
public foo1: string;
public foo2: number;
}
Then In the Module class attributes set the class FooParams to use the values that are a POJO
app.module.ts
import { HttpClientModule, HttpClient } from '#angular/common/http';
import { FooParams } from './foo-params';
#NgModule({
imports : [ HttpClientModule ],
providers: [{
HttpClient
{ provider: FooParams, useValue: params },
{
provider: BarService,
useFactory: setupBarService
deps: [ HttpClient, FooParams ]
}
}]
})
export class AppModule {}
And this is what the class that consumes the pojo would look like.
bar.service.ts
import { HttpClient } from '#angular/common/http';
import { FooParams } from './foo.service.ts';
expecto function setupBarService(http: HttpClient, params: FooParams) {
return new BarService(http, params);
}
#Injectable()
export class BarService {
constructor(http: HttpClient, params: FooParams) {}
//DO STUFF
}
Try by importing import 'core-js/es7/reflect'; in polyfills.ts
I'm trying to get the user ID from the currently logged in user to customize the data loaded from my DataService.
My goal is that:
The AuthGuard should be called before the InboxComponent is loaded
This AuthGuard should set the user variable in the AuthService
I should then be able to use the authService.user in the DataService
However, the console.log(this.authService.user) yields undefined although the console.log(authState) in the AuthGuard correctly logs the user information.
Any clues on what could be wrong?
Authentication Service
export class AuthService {
public user: any;
constructor(private af: AngularFire, public auth$: FirebaseAuth) {
console.log("Auth service state:", this.authState);
}
AuthGuard
#Injectable()
export class AuthGuard implements CanActivate {
constructor(private authService: AuthService, private router: Router) {}
canActivate(): Observable<boolean> {
return this.authService.auth$
.take(1)
.map(authState => {
console.log(authState)
this.authService.setUser(authState);
return !!authState })
.do(authenticated => {
if (!authenticated) {
this.router.navigate(['/error']);
}
});
}
}
Dataservice
export class DataService {
private userPath: string;
constructor(private af: AngularFire, private authService: AuthService) {
console.log(this.authService.user);
}
Router
const routes: Routes = [
{ path: 'inbox', component: InboxComponent, canActivate: [AuthGuard] }
App Module
#NgModule({
declarations: [
InboxComponent
],
imports: [
EmailsModule
],
providers: [
AuthService,
AuthGuard
],
bootstrap: [AppComponent]
})
export class AppModule { }
I finally resolved this. I had a call to my DataService in my app.component.ts file to get some data in my navigation. This is loaded outside the router-module and thus called before the AuthService is called. This in turn caused the DataService before the AuthGuard was called.