I have an Angular 5 application that I want to be able to start from either a mock backend or a real backend depending on an environment variable.
This is my environment file:
export const environment = {
production: false,
mockBackend: true
};
I want to be able to have this sort of structure:
AppModule
SharedAppModules
AppModule, imports SharedAppModules and adds what is necessary.
MockAppModule, imports SharedAppModules and adds what is necessary:
MockBackendService,
MockBackend,
BaseRequestOptions,
{ provide: LocationStrategy, useClass: HashLocationStrategy },
{
provide: Http,
deps: [MockBackend, BaseRequestOptions],
useFactory: mockHttpFactory
}
My goal would be to do in main.ts:
if (environment.production) {
enableProdMode();
}
if(environment.mockBackend) {
platformBrowserDynamic().bootstrapModule(MockAppModule);
} else {
platformBrowserDynamic().bootstrapModule(AppModule);
}
But I have an error from Angular saying it did not find anything to bootstrap. Has anybody already tried to do the same thing or have any idea to achieve the same goal differently?
Related
I'm trying to use NestJS, where I've a .env file at project root level and I'm using import { ConfigModule, ConfigService } from '#nestjs/config';. In order to get the data first I'm loading the .env into app module and then adding mongodb and using the ConfigModule to get DB URI from .env file.
So, my app.module looks something like this.
import { Module } from '#nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { MongooseModule } from '#nestjs/mongoose';
import { ConfigModule, ConfigService } from '#nestjs/config';
import { UsersModule } from './users/users.module';
import configuration from '../configs/configuration';
#Module({
imports: [
ConfigModule.forRoot({
envFilePath: '.env.dev',
isGlobal: true,
load: [configuration],
}),
MongooseModule.forRootAsync({
imports: [ConfigModule],
inject: [ConfigService],
useFactory: async (config: ConfigService) => ({
uri: config.get<string>('MONGODB_URI'),
})
}),
UsersModule
],
controllers: [AppController],
providers: [AppService],
})
export class AppModule {}
But, when I'm trying to hit the port It's throwing an error saying unable to connect to the database.
Can anyone please suggest me where am I making the mistake.
Thanks In advance.
The issue is in .env file use DB connection as mongodb://127.0.0.1:27017/euphoria-dev instead of mongodb://localhost:27017/euphoria-dev
Check the MONGODB_URI once whit console.log(MONGODB_URI) to ensure that this string is read from the file .env File.
error in connecting to the Mongo database is due to the wrong address, which will probably return null.
You can also test from the package #dotenv and use: process.env.MONGODB_URI
good luck.
I want to use environment variables to configure the HttpModule per module, from the docs I can use the configuration like this:
#Module({
imports: [HttpModule.register({
timeout: 5000,
maxRedirects: 5,
})],
})
But I don't know what is the best practice to inclue a baseURL from environment vairable (or a config service), for example like this:
#Module({
imports: [HttpModule.register({
baseURL: this.config.get('API_BASE_URL'),
timeout: 5000,
maxRedirects: 5,
})],
The this.config is undefined here cause it's out of class.
What is the best practice to set baseURL from environment variables (or config service)?
Update Jan 19
HttpModule.registerAsync() was added in version 5.5.0 with this pull request.
HttpModule.registerAsync({
imports:[ConfigModule],
useFactory: async (configService: ConfigService) => ({
baseURL: configService.get('API_BASE_URL'),
timeout: 5000,
maxRedirects: 5,
}),
inject: [ConfigService]
}),
Original Post
This problem was discussed in this issue. For the nestjs modules like the TypeOrmModule or the MongooseModule the following pattern was implemented.
The useFactory method returns the configuration object.
TypeOrmModule.forRootAsync({
imports:[ConfigModule],
useFactory: async (configService: ConfigService) => ({
type: configService.getDatabase()
}),
inject: [ConfigService]
}),
Although Kamil wrote
Above convention is now applied in all nest modules and will be
treated as a best practice (+recommendation for 3rd party modules).
More in the docs
it does not seem to be implemented for the HttpModule yet, but maybe you can open an issue about it. There are also some other suggestions in the issue I mentioned above.
Also have a look at the official docs with best practices on how to implement a ConfigService.
Although the top rated answer to this question is technically correct for most implementations, users of the #nestjs/typeorm package, and the TypeOrmModule should use an implementation that looks more like the below.
// NestJS expects database types to match a type listed in TypeOrmModuleOptions
import { TypeOrmModuleOptions } from '#nestjs/typeorm/dist/interfaces/typeorm-options.interface';
#Module({
imports: [
ConfigModule.forRoot({
isGlobal: true,
load: [mySettingsFactory],
}),
TypeOrmModule.forRootAsync({
imports: [ConfigModule],
useFactory: async (configService: ConfigService) => ({
type: configService.get<TypeOrmModuleOptions>('database.type', {
infer: true, // We also need to infer the type of the database.type variable to make userFactory happy
}),
database: configService.get<string>('database.host'),
entities: [__dirname + '/**/*.entity{.ts,.js}'],
synchronize: true,
logging: true,
}),
inject: [ConfigService],
}),
],
controllers: [],
})
export class AppRoot {
constructor(private connection: Connection) {}
}
The major thing this code is doing is retrieving the correct typings from TypeORM (see the import) and using them to hint the return value configService.get() method. If you don't use the correct TypeORM typings, Typescript would get mad.
I also encountered several issues with implementing a ConfigService as described in the NestJS documentation (no type-safety, no modularity of configuration values, ...), I wrote down our company's final NestJS configuration management strategy in great detail here: NestJS Configuration Management
The basic idea is to have a central config module that loads all configuration values from the processes' environment. However, instead of providing a single service to all modules, each module can inject a dedicated subset of the configuration values! So each module contains a class that specifies all configuration values that this module needs to be provided at runtime. This simultaneously gives the developer type-safe access to configuration values (instead of using string literals throughout the codebase)
Hope this pattern also works for your use-case :)
Great answer by #Kim Kern, which clearly goes over injection of the ConfigService into a module configuration, that might be dependent on environment variables; however, from my personal experience, your app-root module or some other module with a couple of imports might get crowded and/or hard to read as well as understand the imports, module configuration and what the module you are defining relies on. So, thanks to Jay McDoniel, who curated me on this question, you can move configuration logic into a separate file.
First Solution
Example of app.module.ts:
import { Module } from '#nestjs/common';
import { ConfigModule } from '#nestjs/config';
import { MikroOrmModule } from '#mikro-orm/nestjs';
import { AppService } from './users.service';
import { AppController } from './users.controller';
import { get_db_config } from './config/database.config';
#Module({
imports: [
ConfigModule.forRoot({
isGlobal: true,
expandVariables: true,
}),
MikroOrmModule.forRootAsync( get_db_config() ),
],
controllers: [AppController],
providers: [AppService],
})
export class AppModule {}
Example of config/database.config.ts:
import { MikroOrmModuleAsyncOptions } from "#mikro-orm/nestjs";
import { ConfigService } from "#nestjs/config";
export function get_db_config(): MikroOrmModuleAsyncOptions
{
return {
useFactory: (configService: ConfigService) =>
({
dbName: 'driver',
type: 'postgresql',
host: configService.get('DB_HOST'),
port: configService.get('DB_PORT'),
user: configService.get('DB_USERNAME'),
password: configService.get('DB_PASSWORD'),
autoLoadEntities: true
}),
inject: [ConfigService]
}
}
However, NestJS Docs - Configuration Namespaces as well as NestJS Authentication and Authorization Course provide an alternative method of solving this issue.
Second Solution
Example of auth.module.ts:
import { Module } from '#nestjs/common';
import { ConfigModule } from '#nestjs/config';
import { JwtModule } from '#nestjs/jwt';
import jwtConfig from './jwt.config';
#Module({
imports: [
ConfigModule.forFeature( jwtConfig ),
JwtModule.registerAsync( jwtConfig.asProvider() ),
]
})
export class AuthModule {}
Example of jwt.config.ts:
import { registerAs } from "#nestjs/config"
export default registerAs('jwt', () => {
return {
secret: process.env.JWT_SECRET,
issuer: process.env.JWT_TOKEN_ISSUER,
accessTokenTtl: parseInt(process.env.JWT_TOKEN_TTL)
};
});
There's an angular 6 project using environment variables from ./project/src/environments/environment.prod.ts
export const environment = {
production: true,
testVar: 'gg',
};
The backend for this project also has env variables in a .env file, so a lot of variable duplicate angular env variables. It would be nice to have something like
export const environment = {
production: true,
testVar: process.env.TEST_VAR
};
, so I didn't have to duplicate variables.
ie
I'd like to parse variables from a .env file and assign their values to angular env variables during typescript compilation on the server.
How can this be done?
Maybe with webpack?
UPDATE
Some clarification. My .env file contains no json. It looks like this:
TEST_VAR=1
UPDATE
Since ng eject is not available for Angular 6, I don't seem to be able to hack into webpack config. Looks like deadend here.
ng eject
Overview
Temporarily disabled.
Ejects your app and output the
proper webpack configuration and scripts.
This question becomes also more and more important, when we want to containerize angular applications.
My research lead me to an idea, where I have to write a little node.js or typescript program, using dotenv for reading .env file and create the environment.ts file at buildtime, before starting ng serve.
You can create entries in the package.json like this:
...
"config": "ts-node set-env.ts",
"server": "npm run config && ng serve"
...
and run it with
npm run server
Here is a good explanation with an example typescript file:
https://medium.com/#ferie/how-to-pass-environment-variables-at-building-time-in-an-angular-application-using-env-files-4ae1a80383c
You can create a config file and populate in Run-time.
1) create a File(app-config.json) in assets folder with your variables
{ "servicesUrl": "https://localhost:8080/api"}
2) create a service (AppConfigService ) to read the file.
#Injectable()
export class AppConfigService {
private appConfig;
constructor (private injector: Injector) { }
loadAppConfig() {
let http = this.injector.get(HttpClient);
return http.get('/assets/app-config.json')
.toPromise()
.then(data => {
this.appConfig = data;
})
}
get config() {
return this.appConfig;
}
3) Next we need to tell our application to execute the loadAppConfig() method of our service.
import { NgModule, APP_INITIALIZER } from '#angular/core';
import { AppConfigService } from './services/app-config.service';
#NgModule({
...,
providers: [
AppConfigService,
{
provide: APP_INITIALIZER,
useFactory: appInitializerFn,
multi: true,
deps: [AppConfigService]
}
],
...
})
export class AppModule { }
4) create a function called "appInitializerFn" to call our service in AppModule (app.module.ts)
const appInitializerFn = (appConfig: AppConfigService) => {
return () => {
return appConfig.loadAppConfig();
}
};
...
#NgModule({
...
})
export class AppModule {}
5) import environment and use it :example
import { Injectable } from '#angular/core';
import { HttpClient } from '#angular/common/http';
import { AppConfigService } from './services/app-config.service';
#Injectable()
export class DataContextService {
basePath: string;
constructor (private environment: AppConfigService, private http: HttpClient) {
this.basePath = environment.config.servicesBasePath;
}
getNames() {
return this.http.get(this.basePath + '/names/');
}
}
for more information please see:
link
If you want to use variables in build time you could use dotenv
As early as possible in your application, require and configure dotenv.
require('dotenv').config()
Create a .env file in the root directory of your project. Add environment-specific variables on new lines in the form of NAME=VALUE. For example:
DB_HOST=localhost
DB_USER=root
DB_PASS=s1mpl3
I'm working on an ionic2 project and I need to create a new custom JSON config file. I found some tutorials to create one and access it through http.get but I think it's weird to call it through a get request. I want it in the root folder (where all the config JSONs are) and I open/read the file directly.
I don't know if it's possible, or even recommended ? This is why I'm posting here to have some opinions and solutions :)
Thanks
Personally I don't like the read the config.json file by using the http.get way to handle configuration information, and even though there must be another way to just include and read the json file in your code, since we're using Angular2 and Typescript, why not using classes, interfaces and doing it in a more fancy way?
What I'll show you next may seem more complicated than it should at first (although after reading it you will find it very straightforward and easy to understand), but when I started learning Angular2 I saw an example of how they handled config files in the Dependency Injection guide and I followed that in the apps I've worked on to handle config information (like API endpoints, default values, and so on).
According the docs:
Non-class dependencies
[...]
Applications often define configuration objects with lots of small
facts (like the title of the application or the address of a web API
endpoint) but these configuration objects aren't always instances of a
class.
One solution to choosing a provider token for non-class dependencies
is to define and use an OpaqueToken
So you would need to define a config object with the urls and so on, and then an OpaqueToken to be able to use it when injecting the object with your configuration.
I included all my configuration in the app-config.ts file
// Although the ApplicationConfig interface plays no role in dependency injection,
// it supports typing of the configuration object within the class.
export interface ApplicationConfig {
appName: string;
apiEndpoint: string;
}
// Configuration values for our app
export const MY_CONFIG: ApplicationConfig = {
appName: 'My new App',
apiEndpoint: 'http://www...'
};
// Create a config token to avoid naming conflicts
export const MY_CONFIG_TOKEN = new OpaqueToken('config');
What OpaqueToken is may be confusing at first, but it just a string that will avoid naming conflicts when injecting this object. You can find an amazing post about this here.
Then, you just need to include it in the page you need it like this:
import { NavController } from 'ionic-angular/index';
import { Component, OpaqueToken, Injectable, Inject } from "#angular/core";
// Import the config-related things
import { MY_CONFIG_TOKEN, MY_CONFIG, ApplicationConfig } from 'app-config.ts';
#Component({
templateUrl:"home.html",
providers: [{ provide: MY_CONFIG_TOKEN, useValue: MY_CONFIG }]
})
export class HomePage {
private appName: string;
private endPoint: string;
constructor(#Inject(MY_CONFIG_TOKEN) private config: ApplicationConfig) {
this.appName = config.appName;
this.endPoint = config.apiEndpoint;
}
}
Please notice how to include it in the providers array
providers: [{ provide: MY_CONFIG_TOKEN, useValue: MY_CONFIG }]
And how to tell the injector how it should obtain the instance of the config object
#Inject(MY_CONFIG_TOKEN) config: ApplicationConfig
UPDATE
OpaqueToken has been deprecated since v4.0.0 because it does not support type information, use InjectionToken<?> instead.
So instead of these lines:
import { OpaqueToken } from '#angular/core';
// Create a config token to avoid naming conflicts
export const MY_CONFIG_TOKEN = new OpaqueToken('config');
Now we should use
import { InjectionToken } from '#angular/core';
// Create a config token to avoid naming conflicts
export const MY_CONFIG_TOKEN = new InjectionToken<ApplicationConfig>('config');
After reading and reading different solutions I ended up using this hacky implementation. Hopefully there will be a nice and native solution available soon:
import { NgModule } from '#angular/core';
import { environment as devVariables } from './environment.dev';
import { environment as testVariables } from './environment.test';
import { environment as prodVariables } from './environment.prod';
export function environmentFactory() {
const location = window.location.host;
switch (location) {
case 'www.example.org': {
return prodVariables;
}
case 'test.example.org': {
return testVariables;
}
default: {
return devVariables;
}
}
}
#NgModule({
providers: [
{
provide: 'configuration',
useFactory: environmentFactory
}
]
})
export class EnvironmentsModule {}
and then where ever needed, e.g.:
import { Injectable, Injector, Inject } from '#angular/core';
import { AuthenticationService } from '../authentication';
#Injectable()
export class APIService {
private http: Http;
private apiURL: string;
protected authentication: AuthenticationService;
constructor(
public injector: Injector,
#Inject('configuration') public configuration: any
) {
this.http = injector.get(Http);
this.authentication = injector.get(AuthenticationService);
this.apiURL = configuration.apiURL;
};
...
My angular 2.4.9 application A uses Http from #angular/http and another (3rd party) Angular Module B which itself also uses Http.
A -> http
A -> B -> http
In order to add generic HTTP headers, I've created a CustomHttp class in A that extends Http. I've injected it inside my app.module.ts like so
import AppComponent from './app.component';
import { Http, XHRBackend, RequestOptions } from '#angular/http';
import { CustomHttp } from '../myhttp/custom.http';
import { B } from 'some/thirdparty/module';
#NgModule({
bootstrap: [
AppComponent
],
declarations: [
AppComponent
],
imports: [
B.forRoot({ ... /* custom config */ })
],
providers: [{
provide: Http,
deps: [XHRBackend, RequestOptions],
useFactory: (backend: XHRBackend, defaultOptions: RequestOptions) => {
return new CustomHttp(backend, defaultOptions);}
}]
})
This works for all usages of Http inside my application module A. However, it does not work for the B dependency. All usages of Http inside B still seem to use the original class.
I know that DI in Angular works hierarchically https://angular.io/docs/ts/latest/guide/hierarchical-dependency-injection.html.
Is there a way to tell Angular also to inject CustomHttp in the usages of Http in B?