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
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.
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
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.
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.
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);
}