Is there a way to use configService in App.Module.ts? - javascript

I am building RESTful service with NestJs, I have followed the example to build configurations for different environments. It works well for most code. However I am wondering if I can use it in my app.module.ts?
#Module({
imports: [
TypeOrmModule.forRoot({
type: 'mongodb',
host: `${config.get('mongo_url') || 'localhost'}`,
port: 27017,
username: 'a',
password: 'b',
database: 'my_db',
entities: [__dirname + '/MyApp/*.Entity{.ts,.js}'],
synchronize: true}),
MyModule,
ConfigModule,
],
controllers: [],
providers: [MyService],
})
export class AppModule { }
As you can see I do want to move the MongoDb Url info outside the code and I am thinking to leverage .env files. But after some attempts, it does not seem to work.
Of course I can use ${process.env.MONGODB_URL || 'localhost'} instead, and set the environment variables. I'm still curious if I can make the configService work.

You have to use a dynamic import (see Async configuration). With it, you can inject dependencies and use them for the initialization:
TypeOrmModule.forRootAsync({
imports: [ConfigModule],
useFactory: (configService: ConfigService) => ({
type: 'mongodb',
host: configService.databaseHost,
port: configService.databasePort,
username: configService.databaseUsername,
password: configService.databasePassword,
database: configService.databaseName,
entities: [__dirname + '/**/*.entity{.ts,.js}'],
synchronize: true,
}),
inject: [ConfigService],
}),

Related

Reload env variables when k8s configMap changes in Nest.js application

I have a Nest.js based application running in kubernetes. There is ConfigModule initialized in the app and ConfigService that reads env variable defined in configMap.
#Module({
imports: [
ConfigModule.forRoot({
isGlobal: true,
}),
],
controllers: [AppController],
providers: [AppService],
})
export class AppModule {}
#Injectable()
export class AppService {
#Inject(ConfigService)
private config: ConfigService;
getHello(): string {
const app = this.config.get('app-name')
const psw = this.config.get('app-password')
return `name: ${app}, password: ${psw}`;
}
}
configMap.yaml is attached to my container in deployment.yaml:
apiVersion: apps/v1
kind: Deployment
metadata:
name: nestjs-k8s
spec:
selector:
matchLabels:
app: nestjs-k8s
replicas: 1
template:
metadata:
labels:
app: nestjs-k8s
spec:
containers:
- name: nestjs-k8s
image: multiplexor88/nestjs-k8s
ports:
- containerPort: 3000
envFrom:
- configMapRef:
name: config-map
- secretRef:
name: secret
I want to refresh my configuration in application when configMap is changed.
Is there a way to make it?
If it is not possible, then at list is somehow schedule Nest.js ConfigModule to reinitialize without application restart.

Module Federation - No provider for AngularFireAuth

I'm trying to implement Module Federation with Angular and NgRX, but i'm facing a problem and don't know how to fix it.
The problem is: when X application lazy loads a module from Y
application that uses Firebase, angular does not recognize the fire
auth provider.
I have 2 apps: Auth and Dashboard.
My Auth application uses firebase to do user login.
The firebase request login is made by a NgRX effect:
import {AngularFireAuth} from '#angular/fire/auth';
#Injectable()
export class AuthEffects {
userLogin$: Observable<Action> = createEffect(() => {
/* effect implementation */
});
constructor(private fireAuth: AngularFireAuth){}
}
The AuthModule imports:
imports: [
/* ...other imports */
StoreModule.forFeature('authState', authReducer),
EffectsModule.forFeature([AuthEffects]),
AngularFireModule.initializeApp(firebaseConfig),
AngularFireAuthModule
]
The Dashboard AppRoutingModule imports:
const routes: Routes = [
{
path: '',
pathMatch: 'full',
loadChildren: () => import('auth/AuthModule').then(m => m.AuthModule)
}
];
#NgModule({
imports: [
RouterModule.forRoot(routes)
],
exports: [RouterModule]
})
When I start only the Auth application, everything works fine and I can do login.
But when I try to use the Auth application remotely, inside Dashboard application, I get this error:
NullInjectorError: StaticInjectorError(AppModule)[InjectionToken angularfire2.app.options]:
StaticInjectorError(Platform: core)[InjectionToken angularfire2.app.options]:
NullInjectorError: No provider for InjectionToken angularfire2.app.options!
Partial Auth - webpack.config.js
plugins: [
new ModuleFederationPlugin({
name: 'auth',
library: {type: 'var', name: 'auth'},
filename: 'remoteEntry.js',
exposes: {
'./AuthModule': './src/app/login/auth.module.ts',
'./AuthComponent': './src/app/login/auth.component.ts',
'./AuthActions': './src/app/store/actions/auth.actions.ts',
'./AuthReducer': './src/app/store/reducers/auth.reducer.ts',
'./AuthEffects': './src/app/store/effects/auth.effects'
},
shared: {
...dependencies,
'#angular/core': {
requiredVersion: dependencies['#angular/core'],
singleton: true,
},
'#angular/common': {
requiredVersion: dependencies['#angular/common'],
singleton: true,
},
'#angular/router': {
requiredVersion: dependencies['#angular/router'],
singleton: true,
}
}
})
]
Parial Dashboard - webpack.config.js
plugins: [
new ModuleFederationPlugin({
name: 'dashboard',
library: {type: 'var', name: 'dashboard'},
filename: 'remoteEntry.js',
remotes: {
auth: 'auth',
},
shared: {
...dependencies,
'#angular/core': {
requiredVersion: dependencies['#angular/core'],
singleton: true,
},
'#angular/common': {
requiredVersion: dependencies['#angular/common'],
singleton: true,
},
'#angular/router': {
requiredVersion: dependencies['#angular/router'],
singleton: true,
}
}
})
]
I tried to resolve it by myself, but Module Federation is a new thing and we have little posts about.
Can someone help me? If you came until here, thank you very much! :D
Solved!
Thanks Zack Jackson, I could solve the problem.
Solution:
https://github.com/module-federation/module-federation.github.io/issues/14#issuecomment-672647713

Keycloak - how to disable sharing session between two (Angular) client apps?

I have two angular client applications that use Keycloak's angular client for authentication. The problem is, that when i log in on one of them, i'm automatically being logged to another one. I have separate client for each one though.
Keycloak service initialization looks like this in both apps:
#NgModule({
declarations: [AppComponent],
imports: [
AppRoutingModule,
BrowserModule,
KeycloakAngularModule
],
providers: [KeycloakService],
entryComponents: [AppComponent]
})
export class AppModule implements DoBootstrap {
constructor(private keycloakService: KeycloakService) {
}
ngDoBootstrap(app: ApplicationRef) {
this.keycloakService
.init({config: environment.keycloak, initOptions: {onLoad: 'login-required'}})
.then(() => app.bootstrap(AppComponent));
}
}
Keycloak config for the first app:
const keycloakConfig: KeycloakConfig = {
realm: 'quick',
url: 'http://localhost:8000/auth',
clientId: 'quick-ui-customer'
};
export const environment = {
production: false,
keycloak: keycloakConfig,
};
And for the second one:
const keycloakConfig: KeycloakConfig = {
realm: 'quick',
url: 'http://localhost:8000/auth',
clientId: 'quick-ui-employee'
};
export const environment = {
production: false,
keycloak: keycloakConfig,
};
That is a core feature of Single Sign On protocol. But if you don't want to share session, then create clients in different realms.

How can I use interceptor in neo4j-graphql-js endpoint with nestjs framework?

I am using the neo4j-graphql-js library to translate graphql queries to cypher and I need to implement an interceptor to verify that what is returning belongs to the user who is asking for it. For this I need to implement the interceptor but the problem I have is that I have no resolvers since it generates the liberia. How can I make it go through the interceptor? If the interceptor cannot be used, is there a way to implement a middleware in the response?
I'm using nestjs framework. And I use a neo4j database.
Thank you.
Module:
#Module({
imports: [
GraphQLModule.forRootAsync({
useClass: GraphqlConfigService,
}),
],
providers: [neo4jProvider],
})
export class GraphqlModule {
configure(consumer: MiddlewareConsumer) {
consumer
.apply(GraphQLAuthMiddleware,GraphQLRoleMiddleware)
.forRoutes('graphql');
}
}
#Injectable()
export class GraphqlConfigService implements GqlOptionsFactory {
async createGqlOptions(): Promise<GqlModuleOptions> {
const schema = buildSchema();
return {
playground: true,
schema: schema,
path: '/graphql/queries',
context: {
driver: neo4j.driver(
'bolt://neo4j_db:7687',
neo4j.auth.basic('neo4j', 'root')
)
}
};
}
}
function buildSchema(): GraphQLSchema {
return makeAugmentedSchema({
typeDefs,
config: {
query: true,
mutation: true
}
});
}
You can bind your interceptor globally either by using the app.useGlobalInterceptors(MyCustomInterceptor) method in your main.ts or in any module you can add the interceptor in your providers array
#Module({
imports: [/* your imports*/],
providers: [
{
provide: APP_INTERCEPTOR,
useClass: MyCustomInterceptor
},
/* the rest of your providers*/
],
})
export class GraphqlModule {}
APP_INTERCEPTOR is imported from #nestjs/core. Keep in mind, this does bind the interceptor globally. All requests to your server will go through this interceptor.

Angular CLI 6.x environment files for a library

I've created a library with the new CLI 6.x. I've also created an Angular service inside it and I would like to use different URLs for my dev and prod environment. So I created an environment.ts and an environment.prod.ts file in my library folder.
//dev
export const environment = {
production: false,
url: 'dev-url'
};
//prod
export const environment = {
production: true,
url: 'prod-url'
};
I also added the 'fileReplacements' property to the angular.json file:
"configurations": {
"production": {
"fileReplacements": [{
"replace": "projects/tk-shared/src/environments/environment.ts",
"with": "projects/tk-shared/src/environments/environment.prod.ts"
}],
"project": "projects/tk-shared/ng-package.prod.json"
}
}
Compiling the library with the ng build tk-shared works and uses the dev settings, however when compiling with ng build --prod tk-shared I get the following error:
Schema validation failed with the following errors: Data path "" should NOT have additional properties(fileReplacements).
My tip is that the reason is that tk-shared has the projectType: "library" property in angular.json.
Anyway, is it possible to use environment files in a library?
Thanks, #R. Richards for pointing me to the right solution!
These two sources helped me figure out how to do this injection correctly: LINK1 and LINK2.
So what I did ...
Created an InjectionToken and modified my TkSharedModule:
export const BASE_URL = new InjectionToken<string>('BASE_URL');
//...
export class TkSharedModule {
static forRoot(host: string) {
return {
ngModule: TkSharedModule,
providers: [{
provide: BASE_URL,
useValue: host
}]
}
}
}
Provided a value for this token from the environment files in my AppModule:
import {environment} from '../environments/environment';
//...
#NgModule({
declarations: [
AppComponent
],
imports: [
BrowserModule,
TkSharedModule.forRoot(environment.url)
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
And finally injected this value in my service:
//MyServiceService
import { BASE_URL } from '../tk-shared.module';
constructor(#Inject(BASE_URL) BASE_URL: string) {
console.log('base url', BASE_URL);
}

Categories

Resources