celebrate npm package with Nest Framework - javascript

Is there some way to use celebrate NPM package with Nest Framework? In the documentation they only refer to class-validator, but although this has many advantages I have used with Express and other frameworks the celebrate middleware to validate the request. In the case of Nest, the middleware configuration is done in the app.module.ts file, but in others as routing-controllers the decorator #UseBefore is used for the middleware in the controllers, that is why I would appreciate any explanation, example or documentation how to use this middleware with Nest. Thanks!!

The solution:
In a file dedicated to the validations create the middleware with the scheme to use by celebrate (this would be a simple example for an authentication):
import { celebrate, Joi } from 'celebrate';
export const authenticate = celebrate({
headers: Joi.object().keys({
client: Joi.string().required(),
application: Joi.string().required(),
}).unknown(),
body: Joi.object().keys({
email: Joi.string().email().required(),
password: Joi.string().required(),
}),
});
How to use it later? Import it in the module and apply it as a middleware, since celebrate returns a function of type function (req, res, next):
import { Module, NestModule, MiddlewareConsumer, RequestMethod } from '#nestjs/common';
// Others imports ...
import { authenticate } from './validations/authenticate.validation';
#Module({
imports: [ // Your imports... ],
controllers: [ // Your controllers... ],
providers: [ // Your providers... ],
})
export class AuthenticationModule implements NestModule {
configure(consumer: MiddlewareConsumer) {
// Validate Login.
consumer.apply(authenticate).forRoutes({ path: 'security/login', method: RequestMethod.POST });
}
}
The path defined in the configuration must obviously match the endpoint of the controller:
import {
Body,
Post,
Headers,
Controller,
} from '#nestjs/common';
#Controller('security')
export class AuthenticationController {
#Post('login')
async authenticate(
#Body('email') email: string,
#Body('password') password: string,
#Headers('application') application: string): Promise<any> {
// Autenticate user
}
}
References:
https://docs.nestjs.com/middleware
https://www.npmjs.com/package/celebrate

Related

How to inject service inside a module function NestJS

I'm using pino-logger in my NestJS project to log the activities in my application, and I'm logging the object along with ReqId so I can trace the whole activity inside one request. I'd like to use the same "ReqId" in another place as well, but I'm unsure of how to move it outside of the module, so for that, I'm thinking to save that generated ReqId into the CacheManager but not sure how to inject CacheManager class inside genReqId function. Please look over the code below.
app.module.ts
#Module({
imports: [
LoggerModule.forRoot({
pinoHttp: {
genReqId: (req: any) => {
// I'm thinking to use CacheManager here but I'm not sure how to inject CacheManager class here
return req.headers.req_id || uuid(); // from here it generate the request ID and I want to export this ID and use in side an another class
},
base: undefined,
quietReqLogger: true,
timestamp: false,
},
}),
],
})
export class AppModule {}
you need To create sharable service and import it Imports
#Injectable()
export class RequestIdService {
private reqId: string;
setRequestId(reqId: string) {
this.reqId = reqId;
}
getRequestId() {
return this.reqId;
}
}
than import it to logger module
imports: [
LoggerModule.forRoot({
pinoHttp: {
genReqId: (req: any) => {
this.requestIdService.setRequestId(req.headers.req_id || uuid());
return this.requestIdService.getRequestId();
},
base: undefined,
quietReqLogger: true,
timestamp: false,
},
}),
],
providers: [RequestIdService],
```
use that service by
import { RequestIdService } from './request-id.service';
this.requestIdService.getRequestId()

How can I separate the firebase credentials from the production build that I get in Angular 2+? [duplicate]

I want to add an App Settings section into my App where It will contain some consts and pre defined values.
I've already read this answer which uses OpaqueToken But it is deprecated in Angular. This article explains the differences but it didn't provide a full example , and my attempts were unsuccessful.
Here is what I've tried ( I don't know if it's the right way) :
//ServiceAppSettings.ts
import {InjectionToken, OpaqueToken} from "#angular/core";
const CONFIG = {
apiUrl: 'http://my.api.com',
theme: 'suicid-squad',
title: 'My awesome app'
};
const FEATURE_ENABLED = true;
const API_URL = new InjectionToken<string>('apiUrl');
And this is the component where I want to use those consts :
//MainPage.ts
import {...} from '#angular/core'
import {ServiceTest} from "./ServiceTest"
#Component({
selector: 'my-app',
template: `
<span>Hi</span>
` , providers: [
{
provide: ServiceTest,
useFactory: ( apiUrl) => {
// create data service
},
deps: [
new Inject(API_URL)
]
}
]
})
export class MainPage {
}
But it doesn't work and I get errors.
Question:
How can I consume "app.settings" values the Angular way?
plunker
NB Sure I can create Injectable service and put it in the provider of the NgModule , But as I said I want to do it with InjectionToken , the Angular way.
It's not advisable to use the environment.*.ts files for your API URL configuration. It seems like you should because this mentions the word "environment".
Using this is actually compile-time configuration. If you want to change the API URL, you will need to re-build. That's something you don't want to have to do ... just ask your friendly QA department :)
What you need is runtime configuration, i.e. the app loads its configuration when it starts up.
Some other answers touch on this, but the difference is that the configuration needs to be loaded as soon as the app starts, so that it can be used by a normal service whenever it needs it.
To implement runtime configuration:
Add a JSON config file to the /src/assets/ folder (so that is copied on build)
Create an AppConfigService to load and distribute the config
Load the configuration using an APP_INITIALIZER
1. Add Config file to /src/assets
You could add it to another folder, but you'd need to tell the Angular CLI that it is an asset, by updating the angular.json configuration file. Start off using the assets folder:
{
"apiBaseUrl": "https://development.local/apiUrl"
}
2. Create AppConfigService
This is the service which will be injected whenever you need the config value:
import { HttpClient } from '#angular/common/http';
import { Injectable } from '#angular/core';
#Injectable({
providedIn: 'root'
})
export class AppConfigService {
private appConfig: any;
constructor(private http: HttpClient) { }
loadAppConfig() {
return this.http.get('/assets/config.json')
.toPromise()
.then(data => {
this.appConfig = data;
});
}
// This is an example property ... you can make it however you want.
get apiBaseUrl() {
if (!this.appConfig) {
throw Error('Config file not loaded!');
}
return this.appConfig.apiBaseUrl;
}
}
3. Load the configuration using an APP_INITIALIZER
To allow the AppConfigService to be injected safely, with config fully loaded, we need to load the config at app startup time. Importantly, the initialisation factory function needs to return a Promise so that Angular knows to wait until it finishes resolving before finishing startup:
import { APP_INITIALIZER } from '#angular/core';
import { AppConfigService } from './services/app-config.service';
#NgModule({
declarations: [
AppComponent
],
imports: [
BrowserModule,
HttpClientModule
],
providers: [
{
provide: APP_INITIALIZER,
multi: true,
deps: [AppConfigService],
useFactory: (appConfigService: AppConfigService) => {
return () => {
//Make sure to return a promise!
return appConfigService.loadAppConfig();
};
}
}
],
bootstrap: [AppComponent]
})
export class AppModule { }
Now you can inject it wherever you need to and all the config will be ready to read:
#Component({
selector: 'app-test',
templateUrl: './test.component.html',
styleUrls: ['./test.component.scss']
})
export class TestComponent implements OnInit {
apiBaseUrl: string;
constructor(private appConfigService: AppConfigService) {}
ngOnInit(): void {
this.apiBaseUrl = this.appConfigService.apiBaseUrl;
}
}
I can't say it strongly enough, configuring your API urls as compile-time configuration is an anti-pattern. Use runtime configuration.
If you are using angular-cli, there is yet another option:
Angular CLI provides environment files in src/environments (default ones are environment.ts (dev) and environment.prod.ts (production)).
Note that you need to provide the config parameters in all environment.* files, e.g.,
environment.ts:
export const environment = {
production: false,
apiEndpoint: 'http://localhost:8000/api/v1'
};
environment.prod.ts:
export const environment = {
production: true,
apiEndpoint: '__your_production_server__'
};
and use them in your service (the correct environment file is chosen automatically):
api.service.ts
// ... other imports
import { environment } from '../../environments/environment';
#Injectable()
export class ApiService {
public apiRequest(): Observable<MyObject[]> {
const path = environment.apiEndpoint + `/objects`;
// ...
}
// ...
}
Read more on application environments on Github (Angular CLI version 6) or in the official Angular guide (version 7).
I figured out how to do this with InjectionTokens (see example below), and if your project was built using the Angular CLI you can use the environment files found in /environments for static application wide settings like an API endpoint, but depending on your project's requirements you will most likely end up using both since environment files are just object literals, while an injectable configuration using InjectionToken's can use the environment variables and since it's a class can have logic applied to configure it based on other factors in the application, such as initial HTTP request data, subdomain, etc.
Injection Tokens Example
/app/app-config.module.ts
import { NgModule, InjectionToken } from '#angular/core';
import { environment } from '../environments/environment';
export let APP_CONFIG = new InjectionToken<AppConfig>('app.config');
export class AppConfig {
apiEndpoint: string;
}
export const APP_DI_CONFIG: AppConfig = {
apiEndpoint: environment.apiEndpoint
};
#NgModule({
providers: [{
provide: APP_CONFIG,
useValue: APP_DI_CONFIG
}]
})
export class AppConfigModule { }
/app/app.module.ts
import { BrowserModule } from '#angular/platform-browser';
import { NgModule } from '#angular/core';
import { AppConfigModule } from './app-config.module';
#NgModule({
declarations: [
// ...
],
imports: [
// ...
AppConfigModule
],
bootstrap: [AppComponent]
})
export class AppModule { }
Now you can just DI it into any component, service, etc:
/app/core/auth.service.ts
import { Injectable, Inject } from '#angular/core';
import { Http, Response } from '#angular/http';
import { Router } from '#angular/router';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/operator/map';
import 'rxjs/add/operator/catch';
import 'rxjs/add/observable/throw';
import { APP_CONFIG, AppConfig } from '../app-config.module';
import { AuthHttp } from 'angular2-jwt';
#Injectable()
export class AuthService {
constructor(
private http: Http,
private router: Router,
private authHttp: AuthHttp,
#Inject(APP_CONFIG) private config: AppConfig
) { }
/**
* Logs a user into the application.
* #param payload
*/
public login(payload: { username: string, password: string }) {
return this.http
.post(`${this.config.apiEndpoint}/login`, payload)
.map((response: Response) => {
const token = response.json().token;
sessionStorage.setItem('token', token); // TODO: can this be done else where? interceptor
return this.handleResponse(response); // TODO: unset token shouldn't return the token to login
})
.catch(this.handleError);
}
// ...
}
You can then also type check the config using the exported AppConfig.
I found that using an APP_INITIALIZER for this doesn't work in situations where other service providers require the configuration to be injected. They can be instantiated before APP_INITIALIZER is run.
I've seen other solutions that use fetch to read a config.json file and provide it using an injection token in a parameter to platformBrowserDynamic() prior to bootstrapping the root module. But fetch isn't supported in all browsers and in particular WebView browsers for the mobile devices I target.
The following is a solution that works for me for both PWA and mobile devices (WebView). Note: I've only tested in Android so far; working from home means I don't have access to a Mac to build.
In main.ts:
import { enableProdMode } from '#angular/core';
import { platformBrowserDynamic } from '#angular/platform-browser-dynamic';
import { AppModule } from './app/app.module';
import { environment } from './environments/environment';
import { APP_CONFIG } from './app/lib/angular/injection-tokens';
function configListener() {
try {
const configuration = JSON.parse(this.responseText);
// pass config to bootstrap process using an injection token
platformBrowserDynamic([
{ provide: APP_CONFIG, useValue: configuration }
])
.bootstrapModule(AppModule)
.catch(err => console.error(err));
} catch (error) {
console.error(error);
}
}
function configFailed(evt) {
console.error('Error: retrieving config.json');
}
if (environment.production) {
enableProdMode();
}
const request = new XMLHttpRequest();
request.addEventListener('load', configListener);
request.addEventListener('error', configFailed);
request.open('GET', './assets/config/config.json');
request.send();
This code:
kicks off an async request for the config.json file.
When the request completes, parses the JSON into a Javascript object
provides the value using the APP_CONFIG injection token, prior to bootstrapping.
And finally bootstraps the root module.
APP_CONFIG can then be injected into any additional providers in app-module.ts and it will be defined. For example, I can initialise the FIREBASE_OPTIONS injection token from #angular/fire with the following:
{
provide: FIREBASE_OPTIONS,
useFactory: (config: IConfig) => config.firebaseConfig,
deps: [APP_CONFIG]
}
I find this whole thing a surprisingly difficult (and hacky) thing to do for a very common requirement. Hopefully in the near future there will be a better way, such as, support for async provider factories.
The rest of the code for completeness...
In app/lib/angular/injection-tokens.ts:
import { InjectionToken } from '#angular/core';
import { IConfig } from '../config/config';
export const APP_CONFIG = new InjectionToken<IConfig>('app-config');
and in app/lib/config/config.ts I define the interface for my JSON config file:
export interface IConfig {
name: string;
version: string;
instance: string;
firebaseConfig: {
apiKey: string;
// etc
}
}
Config is stored in assets/config/config.json:
{
"name": "my-app",
"version": "#{Build.BuildNumber}#",
"instance": "localdev",
"firebaseConfig": {
"apiKey": "abcd"
...
}
}
Note: I use an Azure DevOps task to insert Build.BuildNumber and substitute other settings for different deployment environments as it is being deployed.
Poor man's configuration file:
Add to your index.html as first line in the body tag:
<script lang="javascript" src="assets/config.js"></script>
Add assets/config.js:
var config = {
apiBaseUrl: "http://localhost:8080"
}
Add config.ts:
export const config: AppConfig = window['config']
export interface AppConfig {
apiBaseUrl: string
}
Here's my solution, loads from .json to allow changes without rebuilding
import { Injectable, Inject } from '#angular/core';
import { Http } from '#angular/http';
import { Observable } from 'rxjs/Observable';
import { Location } from '#angular/common';
#Injectable()
export class ConfigService {
private config: any;
constructor(private location: Location, private http: Http) {
}
async apiUrl(): Promise<string> {
let conf = await this.getConfig();
return Promise.resolve(conf.apiUrl);
}
private async getConfig(): Promise<any> {
if (!this.config) {
this.config = (await this.http.get(this.location.prepareExternalUrl('/assets/config.json')).toPromise()).json();
}
return Promise.resolve(this.config);
}
}
and config.json
{
"apiUrl": "http://localhost:3000/api"
}
Quite a few articles are recommending that you get Angular config settings using an AppConfigService such as this one.
But I found that sometimes this just didn't work.
It was simpler, and more reliable to have a "config.json" file, then create a class which just read it in, and returned a value, eg my config file would look like this:
{
"appName": "Mike's app",
"version": "1.23.4",
"logging_URL" : "https://someWebsite.azurewebsites.net/logs"
}
And I'd access the values using this:
import config from '../../assets/config.json';
#Injectable({
providedIn: 'root'
})
export class AppConfigService {
get appName() {
return config.appName;
}
get appVersion() {
return config.version;
}
get loggingUrl() {
return config.logging_URL;
}
}
(A few months later...)
After patting myself on the back for making a simpler Angular solution, I realised this has a big drawback. If you use an AppConfigService, and you're using CI/CD, then you can get your build process to update the config .json file, and the Angular app will use these settings.
With my version, yes, it's simpler, but there is no config setting file to overwrite. For automated build processes, this might not be desirable.
Here's my two solutions for this
1. Store in json files
Just make a json file and get in your component by $http.get() method. If I was need this very low then it's good and quick.
2. Store by using data services
If you want to store and use in all components or having large usage then it's better to use data service. Like this :
Just create static folder inside src/app folder.
Create a file named as fuels.ts into static folder. You can store other static files here also. Let define your data like this. Assuming you having fuels data.
__
export const Fuels {
Fuel: [
{ "id": 1, "type": "A" },
{ "id": 2, "type": "B" },
{ "id": 3, "type": "C" },
{ "id": 4, "type": "D" },
];
}
Create a file name static.services.ts
__
import { Injectable } from "#angular/core";
import { Fuels } from "./static/fuels";
#Injectable()
export class StaticService {
constructor() { }
getFuelData(): Fuels[] {
return Fuels;
}
}`
Now You can make this available for every module
just import in app.module.ts file like this and change in providers
import { StaticService } from './static.services';
providers: [StaticService]
Now use this as StaticService in any module.
That's All.
We had this problem years ago before I had joined and had in place a solution that used local storage for user and environment information. Angular 1.0 days to be exact. We were formerly dynamically creating a js file at runtime that would then place the generated api urls into a global variable. We're a little more OOP driven these days and don't use local storage for anything.
I created a better solution for both determining environment and api url creation.
How does this differ?
The app will not load unless the config.json file is loaded. It uses factory functions to create a higher degree of SOC. I could encapsulate this into a service, but I never saw any reason when the only similarity between the different sections of the file are that they exist together in the file. Having a factory function allows me to pass the function directly into a module if it's capable of accepting a function. Last, I have an easier time setting up InjectionTokens when factory functions are available to utilize.
Downsides?
You're out of luck using this setup (and most of the other answers) if the module you want to configure doesn't allow a factory function to be passed into either forRoot() or forChild(), and there's no other way to configure the package by using a factory function.
Instructions
Using fetch to retrieve a json file, I store the object in window and raise a custom event. - remember to install whatwg-fetch and add it to your polyfills.ts for IE compatibility
Have an event listener listening for the custom event.
The event listener receives the event, retrieves the object from window to pass to an observable, and clears out what was stored in window.
Bootstrap Angular
-- This is where my solution starts to really differ --
Create a file exporting an interface whose structure represents your config.json -- it really helps with type consistency and the next section of code requires a type, and don't specify {} or any when you know you can specify something more concrete
Create the BehaviorSubject that you will pass the parsed json file into in step 3.
Use factory functions to reference the different sections of your config to maintain SOC
Create InjectionTokens for the providers needing the result of your factory functions
-- and/or --
Pass the factory functions directly into the modules capable of accepting a function in either its forRoot() or forChild() methods.
-- main.ts
I check window["environment"] is not populated before creating an event listener to allow the possiblilty of a solution where window["environment"] is populated by some other means before the code in main.ts ever executes.
import { enableProdMode } from '#angular/core';
import { platformBrowserDynamic } from '#angular/platform-browser-dynamic';
import { AppModule } from './app/app.module';
import { configurationSubject } from './app/utils/environment-resolver';
var configurationLoadedEvent = document.createEvent('Event');
configurationLoadedEvent.initEvent('config-set', true, true);
fetch("../../assets/config.json")
.then(result => { return result.json(); })
.then(data => {
window["environment"] = data;
document.dispatchEvent(configurationLoadedEvent);
}, error => window.location.reload());
/*
angular-cli only loads the first thing it finds it needs a dependency under /app in main.ts when under local scope.
Make AppModule the first dependency it needs and the rest are done for ya. Event listeners are
ran at a higher level of scope bypassing the behavior of not loading AppModule when the
configurationSubject is referenced before calling platformBrowserDynamic().bootstrapModule(AppModule)
example: this will not work because configurationSubject is the first dependency the compiler realizes that lives under
app and will ONLY load that dependency, making AppModule an empty object.
if(window["environment"])
{
if (window["environment"].production) {
enableProdMode();
}
configurationSubject.next(window["environment"]);
platformBrowserDynamic().bootstrapModule(AppModule)
.catch(err => console.log(err));
}
*/
if(!window["environment"]) {
document.addEventListener('config-set', function(e){
if (window["environment"].production) {
enableProdMode();
}
configurationSubject.next(window["environment"]);
window["environment"] = undefined;
platformBrowserDynamic().bootstrapModule(AppModule)
.catch(err => console.log(err));
});
}
--- environment-resolvers.ts
I assign a value to the BehaviorSubject using window["environment"] for redundancy. You could devise a solution where your config is preloaded already and window["environment"] is already populated by the time any of your Angular's app code is ran, including the code in main.ts
import { BehaviorSubject } from "rxjs";
import { IConfig } from "../config.interface";
const config = <IConfig>Object.assign({}, window["environment"]);
export const configurationSubject = new BehaviorSubject<IConfig>(config);
export function resolveEnvironment() {
const env = configurationSubject.getValue().environment;
let resolvedEnvironment = "";
switch (env) {
// case statements for determining whether this is dev, test, stage, or prod
}
return resolvedEnvironment;
}
export function resolveNgxLoggerConfig() {
return configurationSubject.getValue().logging;
}
-- app.module.ts - Stripped down for easier understanding
Fun fact! Older versions of NGXLogger required you to pass in an object into LoggerModule.forRoot(). In fact, the LoggerModule still does! NGXLogger kindly exposes LoggerConfig which you can override allowing you to use a factory function for setup.
import { resolveEnvironment, resolveNgxLoggerConfig, resolveSomethingElse } from './environment-resolvers';
import { LoggerConfig } from 'ngx-logger';
#NgModule({
modules: [
SomeModule.forRoot(resolveSomethingElse)
],
providers:[
{
provide: ENVIRONMENT,
useFactory: resolveEnvironment
},
{
provide: LoggerConfig,
useFactory: resolveNgxLoggerConfig
}
]
})
export class AppModule
Addendum
How did I solve the creation of my API urls?
I wanted to be able to understand what each url did via a comment and wanted typechecking since that's TypeScript's greatest strength compared to javascript (IMO). I also wanted to create an experience for other devs to add new endpoints, and apis that was as seamless as possible.
I created a class that takes in the environment (dev, test, stage, prod, "", and etc) and passed this value to a series of classes[1-N] whose job is to create the base url for each API collection. Each ApiCollection is responsible for creating the base url for each collection of APIs. Could be our own APIs, a vendor's APIs, or even an external link. That class will pass the created base url into each subsequent api it contains. Read the code below to see a bare bones example. Once setup, it's very simple for another dev to add another endpoint to an Api class without having to touch anything else.
TLDR; basic OOP principles and lazy getters for memory optimization
#Injectable({
providedIn: 'root'
})
export class ApiConfig {
public apis: Apis;
constructor(#Inject(ENVIRONMENT) private environment: string) {
this.apis = new Apis(environment);
}
}
export class Apis {
readonly microservices: MicroserviceApiCollection;
constructor(environment: string) {
this.microservices = new MicroserviceApiCollection(environment);
}
}
export abstract class ApiCollection {
protected domain: any;
constructor(environment: string) {
const domain = this.resolveDomain(environment);
Object.defineProperty(ApiCollection.prototype, 'domain', {
get() {
Object.defineProperty(this, 'domain', { value: domain });
return this.domain;
},
configurable: true
});
}
}
export class MicroserviceApiCollection extends ApiCollection {
public member: MemberApi;
constructor(environment) {
super(environment);
this.member = new MemberApi(this.domain);
}
resolveDomain(environment: string): string {
return `https://subdomain${environment}.actualdomain.com/`;
}
}
export class Api {
readonly base: any;
constructor(baseUrl: string) {
Object.defineProperty(this, 'base', {
get() {
Object.defineProperty(this, 'base',
{ value: baseUrl, configurable: true});
return this.base;
},
enumerable: false,
configurable: true
});
}
attachProperty(name: string, value: any, enumerable?: boolean) {
Object.defineProperty(this, name,
{ value, writable: false, configurable: true, enumerable: enumerable || true });
}
}
export class MemberApi extends Api {
/**
* This comment will show up when referencing this.apiConfig.apis.microservices.member.memberInfo
*/
get MemberInfo() {
this.attachProperty("MemberInfo", `${this.base}basic-info`);
return this.MemberInfo;
}
constructor(baseUrl: string) {
super(baseUrl + "member/api/");
}
}
I find this Angular How-to: Editable Config Files from Microsoft Dev blogs being the best solution. You can configure dev build settings or prod build settings.
If running in a container, this article https://pumpingco.de/blog/environment-variables-angular-docker/ shows how to use envsubst on the entry point command to fix this static compile issue on the fly via environment variable.

GraphQLFederationModule healthcheck

I'm learning nest.js and I have an issue I may not completely understand.
In our company we have dev-gateway which checks MY_URL/.well-known/apollo/server-health endpoint to be sure services are up before creating and then download schema from MY_URL. MY_URL is variable we pass to configuration.
So I need to have GET http://MY_URL/.well-known/apollo/server-health to return { status: pass } and POST http://MY_URL/ to return schema/be graphql endpoint.
If path in GraphQLFederationModule config is equal to / it works, but if I have path defined as /graphql then:
GET http://MY_URL/.well-known/apollo/server-health returns { status: pass } and I think it's issue, I wanted graphql service under /graphql path
GET http://MY_URL/graphql/.well-known/apollo/server-health is graphql endpoint and it return error (lack of query) and I think it should return { status: pass }
GET http://MY_URL/graphql returns graphql enpoint which is OK
I prepared some minimal working version and I'm using:
"#apollo/federation": "^0.25.1",
"#nestjs/common": "^7.6.15",
"#nestjs/core": "^7.6.15",
"#nestjs/graphql": "^7.10.3",
"apollo-server-express": "^2.22.2",
"graphql": "^15.5.0",
"graphql-tools": "^7.0.4",
import { NestFactory } from '#nestjs/core';
import { Module } from '#nestjs/common';
import {
GraphQLFederationModule,
Query,
Resolver,
ResolveReference,
Directive, Field, ID, ObjectType
} from '#nestjs/graphql';
import { Controller, Get } from '#nestjs/common';
#Controller('')
export class AppController {
#Get()
healthCheck() {
return 'Hello World!';
}
}
#ObjectType()
#Directive('#key(fields: "_id")')
export class AdSpot {
#Field((type) => ID)
_id: string;
#Field((type) => String)
name: string
}
#Resolver((of) => AdSpot)
export class CatResolver {
#Query((returns) => [AdSpot], { name: 'adSpots' })
async getAdSpots() {
return [];
}
}
#Module({
providers: [CatResolver],
})
export class CatsModule {}
#Module({
imports: [
CatsModule,
GraphQLFederationModule.forRoot({
include: [CatsModule],
path: '/graphql',
autoSchemaFile: true,
sortSchema: true,
playground: true,
disableHealthCheck: false,
}),
],
controllers: [AppController],
})
export class AppModule {}
async function bootstrap() {
try {
const app = await NestFactory.create(AppModule);
await app.listen(3010);
} catch (err) {
console.log('-------------------------------------');
console.log(err);
}
}
bootstrap();
What am I doing wrong? Did I miss some configuration or is it a bug?
Use the relative path i.e
typePaths: ['**/*.graphql'],
In Your case
#Module({
imports: [
CatsModule,
GraphQLFederationModule.forRoot({
include: [CatsModule],
typePaths: ['**/*.graphql'],
autoSchemaFile: true,
sortSchema: true,
playground: true,
disableHealthCheck: false,
}),
],
controllers: [AppController],
})

Cannot find name 'ENV' returnTo: ENV.BASE_URI

I assume my authentication service fails to load a configuration file causing an error.
Im following along a tutorial on AUTH0.Here is the link https://auth0.com/blog/real-world-angular-series-part-2/.
I have to import a file from env.config.ts that will be used by my authentication service.Here is the code env.config.ts
const _isDev = window.location.port.indexOf('4200') > -1;
const getHost = () => {
const protocol = window.location.protocol;
const host = window.location.host;
return `${protocol}//${host}`;
};
const apiURI = _isDev ? 'http://localhost:8083/api/' : `/api/`;
export const ENV = {
BASE_URI: getHost(),
BASE_API: apiURI
};
Here is a quote from the tutorial "This code detects the host environment and sets the app's base URI and base API URI. We'll import this ENV configuration wherever we need to detect and use these URIs."
This env file is imported into a file required by auth0,
Here is my snippet of the auth.config.ts
import { ENV } from './../core/env.config';
interface AuthConfig {
CLIENT_ID: string;
CLIENT_DOMAIN: string;
AUDIENCE: string;
REDIRECT: string;
SCOPE: string;
NAMESPACE: string;
};
export const AUTH_CONFIG: AuthConfig = {
CLIENT_ID: '[xxx]',
CLIENT_DOMAIN: '[]', // e.g., kmaida.auth0.com
AUDIENCE: '[http://localhost:8083/api/]', // e.g., http://localhost:8083/api/
REDIRECT: `${ENV.BASE_URI}/callback`,
SCOPE: 'openid profile email',
NAMESPACE: 'http://myapp.com/roles'
};
Please have a look at the tutorial and maybe share what i could have missed.
The auth.service.ts uses the configutaion file as such
// Remove data from localStorage
this._clearExpiration();
this._clearRedirect();
// End Auth0 authentication session
this._auth0.logout({
clientId: AUTH_CONFIG.CLIENT_ID,
returnTo: ENV.BASE_URI
});
}
this is what my imports section looks like of auth.service.ts
import { Injectable } from '#angular/core';
import { Router } from '#angular/router';
import { BehaviorSubject, Subscription, of, timer } from 'rxjs';
import { mergeMap } from 'rxjs/operators';
import { AUTH_CONFIG } from './auth.config';
import * as auth0 from 'auth0-js';
import { ENV } from './../core/env.config';
The error emmited in my cmd reads
ERROR in src/app/auth/auth.service.ts:92:17 - error TS2304: Cannot find name >'ENV'.
92 returnTo: ENV.BASE_URI.
I also suspect the way the imports are handled from core have a problem ,as i really im not well vast with the slash imports.
I have my app directory that holds the auth and core folder as direct children.
try declaring ENV inside a constructor.
public constructor(){
const env = ENV
}
Then use it.
console.log(env.BASE_URI)

App.settings - the Angular way?

I want to add an App Settings section into my App where It will contain some consts and pre defined values.
I've already read this answer which uses OpaqueToken But it is deprecated in Angular. This article explains the differences but it didn't provide a full example , and my attempts were unsuccessful.
Here is what I've tried ( I don't know if it's the right way) :
//ServiceAppSettings.ts
import {InjectionToken, OpaqueToken} from "#angular/core";
const CONFIG = {
apiUrl: 'http://my.api.com',
theme: 'suicid-squad',
title: 'My awesome app'
};
const FEATURE_ENABLED = true;
const API_URL = new InjectionToken<string>('apiUrl');
And this is the component where I want to use those consts :
//MainPage.ts
import {...} from '#angular/core'
import {ServiceTest} from "./ServiceTest"
#Component({
selector: 'my-app',
template: `
<span>Hi</span>
` , providers: [
{
provide: ServiceTest,
useFactory: ( apiUrl) => {
// create data service
},
deps: [
new Inject(API_URL)
]
}
]
})
export class MainPage {
}
But it doesn't work and I get errors.
Question:
How can I consume "app.settings" values the Angular way?
plunker
NB Sure I can create Injectable service and put it in the provider of the NgModule , But as I said I want to do it with InjectionToken , the Angular way.
It's not advisable to use the environment.*.ts files for your API URL configuration. It seems like you should because this mentions the word "environment".
Using this is actually compile-time configuration. If you want to change the API URL, you will need to re-build. That's something you don't want to have to do ... just ask your friendly QA department :)
What you need is runtime configuration, i.e. the app loads its configuration when it starts up.
Some other answers touch on this, but the difference is that the configuration needs to be loaded as soon as the app starts, so that it can be used by a normal service whenever it needs it.
To implement runtime configuration:
Add a JSON config file to the /src/assets/ folder (so that is copied on build)
Create an AppConfigService to load and distribute the config
Load the configuration using an APP_INITIALIZER
1. Add Config file to /src/assets
You could add it to another folder, but you'd need to tell the Angular CLI that it is an asset, by updating the angular.json configuration file. Start off using the assets folder:
{
"apiBaseUrl": "https://development.local/apiUrl"
}
2. Create AppConfigService
This is the service which will be injected whenever you need the config value:
import { HttpClient } from '#angular/common/http';
import { Injectable } from '#angular/core';
#Injectable({
providedIn: 'root'
})
export class AppConfigService {
private appConfig: any;
constructor(private http: HttpClient) { }
loadAppConfig() {
return this.http.get('/assets/config.json')
.toPromise()
.then(data => {
this.appConfig = data;
});
}
// This is an example property ... you can make it however you want.
get apiBaseUrl() {
if (!this.appConfig) {
throw Error('Config file not loaded!');
}
return this.appConfig.apiBaseUrl;
}
}
3. Load the configuration using an APP_INITIALIZER
To allow the AppConfigService to be injected safely, with config fully loaded, we need to load the config at app startup time. Importantly, the initialisation factory function needs to return a Promise so that Angular knows to wait until it finishes resolving before finishing startup:
import { APP_INITIALIZER } from '#angular/core';
import { AppConfigService } from './services/app-config.service';
#NgModule({
declarations: [
AppComponent
],
imports: [
BrowserModule,
HttpClientModule
],
providers: [
{
provide: APP_INITIALIZER,
multi: true,
deps: [AppConfigService],
useFactory: (appConfigService: AppConfigService) => {
return () => {
//Make sure to return a promise!
return appConfigService.loadAppConfig();
};
}
}
],
bootstrap: [AppComponent]
})
export class AppModule { }
Now you can inject it wherever you need to and all the config will be ready to read:
#Component({
selector: 'app-test',
templateUrl: './test.component.html',
styleUrls: ['./test.component.scss']
})
export class TestComponent implements OnInit {
apiBaseUrl: string;
constructor(private appConfigService: AppConfigService) {}
ngOnInit(): void {
this.apiBaseUrl = this.appConfigService.apiBaseUrl;
}
}
I can't say it strongly enough, configuring your API urls as compile-time configuration is an anti-pattern. Use runtime configuration.
If you are using angular-cli, there is yet another option:
Angular CLI provides environment files in src/environments (default ones are environment.ts (dev) and environment.prod.ts (production)).
Note that you need to provide the config parameters in all environment.* files, e.g.,
environment.ts:
export const environment = {
production: false,
apiEndpoint: 'http://localhost:8000/api/v1'
};
environment.prod.ts:
export const environment = {
production: true,
apiEndpoint: '__your_production_server__'
};
and use them in your service (the correct environment file is chosen automatically):
api.service.ts
// ... other imports
import { environment } from '../../environments/environment';
#Injectable()
export class ApiService {
public apiRequest(): Observable<MyObject[]> {
const path = environment.apiEndpoint + `/objects`;
// ...
}
// ...
}
Read more on application environments on Github (Angular CLI version 6) or in the official Angular guide (version 7).
I figured out how to do this with InjectionTokens (see example below), and if your project was built using the Angular CLI you can use the environment files found in /environments for static application wide settings like an API endpoint, but depending on your project's requirements you will most likely end up using both since environment files are just object literals, while an injectable configuration using InjectionToken's can use the environment variables and since it's a class can have logic applied to configure it based on other factors in the application, such as initial HTTP request data, subdomain, etc.
Injection Tokens Example
/app/app-config.module.ts
import { NgModule, InjectionToken } from '#angular/core';
import { environment } from '../environments/environment';
export let APP_CONFIG = new InjectionToken<AppConfig>('app.config');
export class AppConfig {
apiEndpoint: string;
}
export const APP_DI_CONFIG: AppConfig = {
apiEndpoint: environment.apiEndpoint
};
#NgModule({
providers: [{
provide: APP_CONFIG,
useValue: APP_DI_CONFIG
}]
})
export class AppConfigModule { }
/app/app.module.ts
import { BrowserModule } from '#angular/platform-browser';
import { NgModule } from '#angular/core';
import { AppConfigModule } from './app-config.module';
#NgModule({
declarations: [
// ...
],
imports: [
// ...
AppConfigModule
],
bootstrap: [AppComponent]
})
export class AppModule { }
Now you can just DI it into any component, service, etc:
/app/core/auth.service.ts
import { Injectable, Inject } from '#angular/core';
import { Http, Response } from '#angular/http';
import { Router } from '#angular/router';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/operator/map';
import 'rxjs/add/operator/catch';
import 'rxjs/add/observable/throw';
import { APP_CONFIG, AppConfig } from '../app-config.module';
import { AuthHttp } from 'angular2-jwt';
#Injectable()
export class AuthService {
constructor(
private http: Http,
private router: Router,
private authHttp: AuthHttp,
#Inject(APP_CONFIG) private config: AppConfig
) { }
/**
* Logs a user into the application.
* #param payload
*/
public login(payload: { username: string, password: string }) {
return this.http
.post(`${this.config.apiEndpoint}/login`, payload)
.map((response: Response) => {
const token = response.json().token;
sessionStorage.setItem('token', token); // TODO: can this be done else where? interceptor
return this.handleResponse(response); // TODO: unset token shouldn't return the token to login
})
.catch(this.handleError);
}
// ...
}
You can then also type check the config using the exported AppConfig.
I found that using an APP_INITIALIZER for this doesn't work in situations where other service providers require the configuration to be injected. They can be instantiated before APP_INITIALIZER is run.
I've seen other solutions that use fetch to read a config.json file and provide it using an injection token in a parameter to platformBrowserDynamic() prior to bootstrapping the root module. But fetch isn't supported in all browsers and in particular WebView browsers for the mobile devices I target.
The following is a solution that works for me for both PWA and mobile devices (WebView). Note: I've only tested in Android so far; working from home means I don't have access to a Mac to build.
In main.ts:
import { enableProdMode } from '#angular/core';
import { platformBrowserDynamic } from '#angular/platform-browser-dynamic';
import { AppModule } from './app/app.module';
import { environment } from './environments/environment';
import { APP_CONFIG } from './app/lib/angular/injection-tokens';
function configListener() {
try {
const configuration = JSON.parse(this.responseText);
// pass config to bootstrap process using an injection token
platformBrowserDynamic([
{ provide: APP_CONFIG, useValue: configuration }
])
.bootstrapModule(AppModule)
.catch(err => console.error(err));
} catch (error) {
console.error(error);
}
}
function configFailed(evt) {
console.error('Error: retrieving config.json');
}
if (environment.production) {
enableProdMode();
}
const request = new XMLHttpRequest();
request.addEventListener('load', configListener);
request.addEventListener('error', configFailed);
request.open('GET', './assets/config/config.json');
request.send();
This code:
kicks off an async request for the config.json file.
When the request completes, parses the JSON into a Javascript object
provides the value using the APP_CONFIG injection token, prior to bootstrapping.
And finally bootstraps the root module.
APP_CONFIG can then be injected into any additional providers in app-module.ts and it will be defined. For example, I can initialise the FIREBASE_OPTIONS injection token from #angular/fire with the following:
{
provide: FIREBASE_OPTIONS,
useFactory: (config: IConfig) => config.firebaseConfig,
deps: [APP_CONFIG]
}
I find this whole thing a surprisingly difficult (and hacky) thing to do for a very common requirement. Hopefully in the near future there will be a better way, such as, support for async provider factories.
The rest of the code for completeness...
In app/lib/angular/injection-tokens.ts:
import { InjectionToken } from '#angular/core';
import { IConfig } from '../config/config';
export const APP_CONFIG = new InjectionToken<IConfig>('app-config');
and in app/lib/config/config.ts I define the interface for my JSON config file:
export interface IConfig {
name: string;
version: string;
instance: string;
firebaseConfig: {
apiKey: string;
// etc
}
}
Config is stored in assets/config/config.json:
{
"name": "my-app",
"version": "#{Build.BuildNumber}#",
"instance": "localdev",
"firebaseConfig": {
"apiKey": "abcd"
...
}
}
Note: I use an Azure DevOps task to insert Build.BuildNumber and substitute other settings for different deployment environments as it is being deployed.
Poor man's configuration file:
Add to your index.html as first line in the body tag:
<script lang="javascript" src="assets/config.js"></script>
Add assets/config.js:
var config = {
apiBaseUrl: "http://localhost:8080"
}
Add config.ts:
export const config: AppConfig = window['config']
export interface AppConfig {
apiBaseUrl: string
}
Here's my solution, loads from .json to allow changes without rebuilding
import { Injectable, Inject } from '#angular/core';
import { Http } from '#angular/http';
import { Observable } from 'rxjs/Observable';
import { Location } from '#angular/common';
#Injectable()
export class ConfigService {
private config: any;
constructor(private location: Location, private http: Http) {
}
async apiUrl(): Promise<string> {
let conf = await this.getConfig();
return Promise.resolve(conf.apiUrl);
}
private async getConfig(): Promise<any> {
if (!this.config) {
this.config = (await this.http.get(this.location.prepareExternalUrl('/assets/config.json')).toPromise()).json();
}
return Promise.resolve(this.config);
}
}
and config.json
{
"apiUrl": "http://localhost:3000/api"
}
Quite a few articles are recommending that you get Angular config settings using an AppConfigService such as this one.
But I found that sometimes this just didn't work.
It was simpler, and more reliable to have a "config.json" file, then create a class which just read it in, and returned a value, eg my config file would look like this:
{
"appName": "Mike's app",
"version": "1.23.4",
"logging_URL" : "https://someWebsite.azurewebsites.net/logs"
}
And I'd access the values using this:
import config from '../../assets/config.json';
#Injectable({
providedIn: 'root'
})
export class AppConfigService {
get appName() {
return config.appName;
}
get appVersion() {
return config.version;
}
get loggingUrl() {
return config.logging_URL;
}
}
(A few months later...)
After patting myself on the back for making a simpler Angular solution, I realised this has a big drawback. If you use an AppConfigService, and you're using CI/CD, then you can get your build process to update the config .json file, and the Angular app will use these settings.
With my version, yes, it's simpler, but there is no config setting file to overwrite. For automated build processes, this might not be desirable.
Here's my two solutions for this
1. Store in json files
Just make a json file and get in your component by $http.get() method. If I was need this very low then it's good and quick.
2. Store by using data services
If you want to store and use in all components or having large usage then it's better to use data service. Like this :
Just create static folder inside src/app folder.
Create a file named as fuels.ts into static folder. You can store other static files here also. Let define your data like this. Assuming you having fuels data.
__
export const Fuels {
Fuel: [
{ "id": 1, "type": "A" },
{ "id": 2, "type": "B" },
{ "id": 3, "type": "C" },
{ "id": 4, "type": "D" },
];
}
Create a file name static.services.ts
__
import { Injectable } from "#angular/core";
import { Fuels } from "./static/fuels";
#Injectable()
export class StaticService {
constructor() { }
getFuelData(): Fuels[] {
return Fuels;
}
}`
Now You can make this available for every module
just import in app.module.ts file like this and change in providers
import { StaticService } from './static.services';
providers: [StaticService]
Now use this as StaticService in any module.
That's All.
We had this problem years ago before I had joined and had in place a solution that used local storage for user and environment information. Angular 1.0 days to be exact. We were formerly dynamically creating a js file at runtime that would then place the generated api urls into a global variable. We're a little more OOP driven these days and don't use local storage for anything.
I created a better solution for both determining environment and api url creation.
How does this differ?
The app will not load unless the config.json file is loaded. It uses factory functions to create a higher degree of SOC. I could encapsulate this into a service, but I never saw any reason when the only similarity between the different sections of the file are that they exist together in the file. Having a factory function allows me to pass the function directly into a module if it's capable of accepting a function. Last, I have an easier time setting up InjectionTokens when factory functions are available to utilize.
Downsides?
You're out of luck using this setup (and most of the other answers) if the module you want to configure doesn't allow a factory function to be passed into either forRoot() or forChild(), and there's no other way to configure the package by using a factory function.
Instructions
Using fetch to retrieve a json file, I store the object in window and raise a custom event. - remember to install whatwg-fetch and add it to your polyfills.ts for IE compatibility
Have an event listener listening for the custom event.
The event listener receives the event, retrieves the object from window to pass to an observable, and clears out what was stored in window.
Bootstrap Angular
-- This is where my solution starts to really differ --
Create a file exporting an interface whose structure represents your config.json -- it really helps with type consistency and the next section of code requires a type, and don't specify {} or any when you know you can specify something more concrete
Create the BehaviorSubject that you will pass the parsed json file into in step 3.
Use factory functions to reference the different sections of your config to maintain SOC
Create InjectionTokens for the providers needing the result of your factory functions
-- and/or --
Pass the factory functions directly into the modules capable of accepting a function in either its forRoot() or forChild() methods.
-- main.ts
I check window["environment"] is not populated before creating an event listener to allow the possiblilty of a solution where window["environment"] is populated by some other means before the code in main.ts ever executes.
import { enableProdMode } from '#angular/core';
import { platformBrowserDynamic } from '#angular/platform-browser-dynamic';
import { AppModule } from './app/app.module';
import { configurationSubject } from './app/utils/environment-resolver';
var configurationLoadedEvent = document.createEvent('Event');
configurationLoadedEvent.initEvent('config-set', true, true);
fetch("../../assets/config.json")
.then(result => { return result.json(); })
.then(data => {
window["environment"] = data;
document.dispatchEvent(configurationLoadedEvent);
}, error => window.location.reload());
/*
angular-cli only loads the first thing it finds it needs a dependency under /app in main.ts when under local scope.
Make AppModule the first dependency it needs and the rest are done for ya. Event listeners are
ran at a higher level of scope bypassing the behavior of not loading AppModule when the
configurationSubject is referenced before calling platformBrowserDynamic().bootstrapModule(AppModule)
example: this will not work because configurationSubject is the first dependency the compiler realizes that lives under
app and will ONLY load that dependency, making AppModule an empty object.
if(window["environment"])
{
if (window["environment"].production) {
enableProdMode();
}
configurationSubject.next(window["environment"]);
platformBrowserDynamic().bootstrapModule(AppModule)
.catch(err => console.log(err));
}
*/
if(!window["environment"]) {
document.addEventListener('config-set', function(e){
if (window["environment"].production) {
enableProdMode();
}
configurationSubject.next(window["environment"]);
window["environment"] = undefined;
platformBrowserDynamic().bootstrapModule(AppModule)
.catch(err => console.log(err));
});
}
--- environment-resolvers.ts
I assign a value to the BehaviorSubject using window["environment"] for redundancy. You could devise a solution where your config is preloaded already and window["environment"] is already populated by the time any of your Angular's app code is ran, including the code in main.ts
import { BehaviorSubject } from "rxjs";
import { IConfig } from "../config.interface";
const config = <IConfig>Object.assign({}, window["environment"]);
export const configurationSubject = new BehaviorSubject<IConfig>(config);
export function resolveEnvironment() {
const env = configurationSubject.getValue().environment;
let resolvedEnvironment = "";
switch (env) {
// case statements for determining whether this is dev, test, stage, or prod
}
return resolvedEnvironment;
}
export function resolveNgxLoggerConfig() {
return configurationSubject.getValue().logging;
}
-- app.module.ts - Stripped down for easier understanding
Fun fact! Older versions of NGXLogger required you to pass in an object into LoggerModule.forRoot(). In fact, the LoggerModule still does! NGXLogger kindly exposes LoggerConfig which you can override allowing you to use a factory function for setup.
import { resolveEnvironment, resolveNgxLoggerConfig, resolveSomethingElse } from './environment-resolvers';
import { LoggerConfig } from 'ngx-logger';
#NgModule({
modules: [
SomeModule.forRoot(resolveSomethingElse)
],
providers:[
{
provide: ENVIRONMENT,
useFactory: resolveEnvironment
},
{
provide: LoggerConfig,
useFactory: resolveNgxLoggerConfig
}
]
})
export class AppModule
Addendum
How did I solve the creation of my API urls?
I wanted to be able to understand what each url did via a comment and wanted typechecking since that's TypeScript's greatest strength compared to javascript (IMO). I also wanted to create an experience for other devs to add new endpoints, and apis that was as seamless as possible.
I created a class that takes in the environment (dev, test, stage, prod, "", and etc) and passed this value to a series of classes[1-N] whose job is to create the base url for each API collection. Each ApiCollection is responsible for creating the base url for each collection of APIs. Could be our own APIs, a vendor's APIs, or even an external link. That class will pass the created base url into each subsequent api it contains. Read the code below to see a bare bones example. Once setup, it's very simple for another dev to add another endpoint to an Api class without having to touch anything else.
TLDR; basic OOP principles and lazy getters for memory optimization
#Injectable({
providedIn: 'root'
})
export class ApiConfig {
public apis: Apis;
constructor(#Inject(ENVIRONMENT) private environment: string) {
this.apis = new Apis(environment);
}
}
export class Apis {
readonly microservices: MicroserviceApiCollection;
constructor(environment: string) {
this.microservices = new MicroserviceApiCollection(environment);
}
}
export abstract class ApiCollection {
protected domain: any;
constructor(environment: string) {
const domain = this.resolveDomain(environment);
Object.defineProperty(ApiCollection.prototype, 'domain', {
get() {
Object.defineProperty(this, 'domain', { value: domain });
return this.domain;
},
configurable: true
});
}
}
export class MicroserviceApiCollection extends ApiCollection {
public member: MemberApi;
constructor(environment) {
super(environment);
this.member = new MemberApi(this.domain);
}
resolveDomain(environment: string): string {
return `https://subdomain${environment}.actualdomain.com/`;
}
}
export class Api {
readonly base: any;
constructor(baseUrl: string) {
Object.defineProperty(this, 'base', {
get() {
Object.defineProperty(this, 'base',
{ value: baseUrl, configurable: true});
return this.base;
},
enumerable: false,
configurable: true
});
}
attachProperty(name: string, value: any, enumerable?: boolean) {
Object.defineProperty(this, name,
{ value, writable: false, configurable: true, enumerable: enumerable || true });
}
}
export class MemberApi extends Api {
/**
* This comment will show up when referencing this.apiConfig.apis.microservices.member.memberInfo
*/
get MemberInfo() {
this.attachProperty("MemberInfo", `${this.base}basic-info`);
return this.MemberInfo;
}
constructor(baseUrl: string) {
super(baseUrl + "member/api/");
}
}
I find this Angular How-to: Editable Config Files from Microsoft Dev blogs being the best solution. You can configure dev build settings or prod build settings.
If running in a container, this article https://pumpingco.de/blog/environment-variables-angular-docker/ shows how to use envsubst on the entry point command to fix this static compile issue on the fly via environment variable.

Categories