Firebase Angular 4 Initialize based on node environment - node.js

I've built my Angular 4 project using the Angular CLI.
I am deploying my app on Heroku, I've created heroku pipelines for dev and production environment.
I have two firebase database dev and production and I want my angular 2 apps to connect to the firebase database based on heroku config variables
I searched on google and found this answer helpful as #yoni-rabinovitch suggested to send an HTTP request on node server to check for the environment when the app initializes.
I am a beginner in Angular 4 and typescript and all I need to implement is to send an HTTP request and initialize the firebase module based on the response.
app.module.ts
import { environment } from '../environments/environment';
#NgModule({
declarations: [
AppComponent
],
imports: [
BrowserModule,
HttpClientModule,
FormsModule,
ReactiveFormsModule,
BrowserAnimationsModule,
AngularFireModule.initializeApp(environment.firebase)
],
bootstrap: [AppComponent]
})
export class AppModule {}
Any help would be highly appreciated

I had to create an API on my server and set the function below in environments.ts file.
export let environment: any = {
production: false,
firebase: {...}
};
export const setEnvironment = async () => {
try {
const response = await fetch('api/checkEnvironment');
const json = await response.json();
if (json.data?.isPROD) {
// Prod Environment
environment = {
production: true,
firebase: {...}
};
}
} catch (error) {
throw error;
}
};
and call it from main.ts file
import {
environment,
setEnvironment,
} from './environments/environment';
import { enableProdMode } from '#angular/core';
import { platformBrowserDynamic } from '#angular/platform-browser-dynamic';
import { AppModule } from './app/app.module';
import { FIREBASE_OPTIONS } from '#angular/fire';
(async () => {
await setEnvironment();
if (environment.production) {
enableProdMode();
}
platformBrowserDynamic([
{
provide: FIREBASE_OPTIONS,
useValue: environment.firebase,
},
])
.bootstrapModule(AppModule)
.catch((err) => console.error(err));
})();

You can create a function to receive the configuration:
import { environment } from '../environments/environment';
#NgModule({
declarations: [
AppComponent
],
imports: [
BrowserModule,
HttpClientModule,
FormsModule,
ReactiveFormsModule,
BrowserAnimationsModule,
AngularFireModule.initializeApp(AppModule.getFirebaseConfig())
],
bootstrap: [AppComponent]
})
export class AppModule {
static getFirebaseConfig(): FirebaseAppConfig {
// do http request
// return correct correct configuration depending on http request
return environment.firebase;
}
}

Related

How do I serve static files in NestJS using fastify?

How does one serve static files in NestJS using fastify? I can't seem to find any recent examples of setting this up properly. I have my main.ts set up like this:
main.ts
// This must be the first thing imported in the app
import 'src/tracing';
import * as winston from 'winston';
import fastifyStatic, { FastifyStaticOptions } from '#fastify/static';
import { NestFactory } from '#nestjs/core';
import {
FastifyAdapter,
NestFastifyApplication,
} from '#nestjs/platform-fastify';
import { path } from 'app-root-path';
import { WinstonModule } from 'nest-winston';
import { doc } from 'prettier';
import { AppModule } from 'src/app.module';
import join = doc.builders.join;
async function bootstrap() {
const app = await NestFactory.create<NestFastifyApplication>(
AppModule,
new FastifyAdapter(),
{
logger: WinstonModule.createLogger({
format: winston.format.combine(
winston.format.timestamp(),
winston.format.json(),
),
transports: [new winston.transports.Console()],
}),
rawBody: true,
},
);
await app.register(require('#fastify/static'), {
root: require('app-root-path').resolve('/client'),
prefix: '/client/', // optional: default '/'
});
// eslint-disable-next-line #typescript-eslint/ban-ts-comment
// #ts-ignore
app.get('/another/path', function (req, reply) {
reply.sendFile('index.html');
});
app.enableShutdownHooks(); // terminus needs this to listen for SIGTERM/SIGKILL
await app.listen(3002, '0.0.0.0');
console.log(`Application is running on: ${await app.getUrl()}`);
}
bootstrap();
The static file I'm attempting to serve is client/index.html.
However, when I run my app I get the following error: Nest could not find /another/path element (this provider does not exist in the current context).
I've also tried setting up my app.module.ts Modules like this:
app.module.ts
#Module({
imports: [
...configModules,
...domainModules,
...libraryModules,
ServeStaticModule.forRoot({
rootPath: require('app-root-path').resolve('/client'),
renderPath: '/client/*',
}),
],
controllers: [AppController],
providers: [AppService],
})
This leads to the following error:
/Users/ewu/Desktop/Projects/janus/node_modules/#nestjs/platform-fastify/node_modules/fastify/lib/route.js:286
throw new FST_ERR_DUPLICATED_ROUTE(opts.method, opts.url)
^
FastifyError: Method 'HEAD' already declared for route '/'
at Object.addNewRoute (/Users/ewu/Desktop/Projects/janus/node_modules/#nestjs/platform-fastify/node_modules/fastify/lib/route.js:286:19)
at Object.route (/Users/ewu/Desktop/Projects/janus/node_modules/#nestjs/platform-fastify/node_modules/fastify/lib/route.js:211:19)
at Object.prepareRoute (/Users/ewu/Desktop/Projects/janus/node_modules/#nestjs/platform-fastify/node_modules/fastify/lib/route.js:144:18)
at Object._head [as head] (/Users/ewu/Desktop/Projects/janus/node_modules/#nestjs/platform-fastify/node_modules/fastify/fastify.js:247:34)
at fastifyStatic (/Users/ewu/Desktop/Projects/janus/node_modules/#fastify/static/index.js:370:17)
Here are the relevant packages and their versions:
"#nestjs/serve-static": "^3.0.0",
"fastify-static": "^4.7.0",
"fastify": "^4.8.1",
"#nestjs/platform-fastify": "^9.1.2",
"#fastify/static": "^6.0.0",
I'm using version 9.0.0 of Nest and v16.15.0 of Node.
You most likely have a #Get() under a #Controller() (most likely your AppController) which is binding the GET / route already. Fastify won't let you bind two handlers to the same route. Because of this, you either need to change the #Get() to have some sort of route associated with it, change the ServeStaticModule to have a different served route, or use a global prefix to modify the rest of the server routes (I believe this leaves the server static module unaffected).

NestJs - ConfigModule.forRoot isGlobal not working

I am trying to load the "process.env.AUTH_SECRET" in AuthModule, but it is giving me the undefined error "Error: secretOrPrivateKey must have a value".
I did setup the "isGlobal: true" in AppModule and it was able to read "process.env.MONGO_URL" there fine.
I have also installed dotenv, which reads fine if I do:
import * as dotenv from 'dotenv';
dotenv.config();
export const jwtConstants = {
secret: process.env.AUTH_SECRET,
};
But I would rather do it the "NestJs" way as the doc says adding isGlobal should make the env available to all other modules.
auth.module.ts
import { Module } from '#nestjs/common';
import { AuthService } from './auth.service';
import { UserModule } from '../user/user.module';
import { PassportModule } from '#nestjs/passport';
import { LocalStrategy } from './local.strategy';
import { JwtModule } from '#nestjs/jwt';
#Module({
imports: [
UserModule,
PassportModule,
JwtModule.register({
secret: process.env.AUTH_SECRET, //Cannot read this.
signOptions: { expiresIn: '60s' },
}),
],
providers: [AuthService, LocalStrategy],
exports: [AuthService, JwtModule],
})
export class AuthModule {}
app.module.ts
import { Module } from '#nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { UserModule } from './user/user.module';
import { MongooseModule } from '#nestjs/mongoose';
import { ConfigModule } from '#nestjs/config';
import { AuthModule } from './auth/auth.module';
#Module({
imports: [
ConfigModule.forRoot({
isGlobal: true,
}),
UserModule,
MongooseModule.forRoot(process.env.MONGO_URL), // Can read this fine
AuthModule,
],
controllers: [AppController],
providers: [AppService],
})
export class AppModule {}
What am I missing or doing wrong? Just for reference, I am trying to follow this authentication tutorial.
Thank you,
Your MongooseModule and AuthModule are dependent on your ConfigModule, but they don't know it.
Your modules are busy being loaded up, however your ConfigModule has to preform an async i/o process (reading your .env) before it can be ready. Meanwhile, Nest carries on loading without waiting for ConfigModule to finish it's job unless another it finds another module is dependent on one of it's exports.
This is where a modules *Async flavoured methods come in to play. They give you control over the modules instantiation. In this context, they allow us to inject the ConfigService from the ConfigModule which will not happen until the ConfigService is ready.
So changing your JWTModule configuration from using .register to .registerAsync to inject the ConfigService is what you are after:
JWTModule.registerAsync({
inject: [ConfigService],
useFactory: (config: ConfigService) => {
secret: config.get<string>('AUTH_SECRET'),
signOptions: { expiresIn: '60s' }
}
})
Now, JWTModule will not load until ConfigService is ready and available in scope. You will most likely need to do that for your MongooseModule too.
That is the "NestJS" way.
Saying that, if all you really needed to do was have your .env loaded into process.env, put:
import * as dotenv from 'dotenv';
import { resolve } from 'path';
dotenv.config({ path: resolve(__dirname, '../.env') });
At the start of your main.ts file. That would lead to dotenv synchronously loading it before anything else happened

NestJS: Unit Test Joi Validation

I'am new using Node and Nest framework, and I have a problem when testing modules that import a Custom Config Module that uses Joi to validate env vars:
yarn test
Test suite failed to run
Config validation error: "APP_PORT" is required
app-config.module.ts
#Module({
imports: [
ConfigModule.forRoot({
expandVariables: true,
load: [configuration],
validationSchema: Joi.object({
APP_PORT: Joi.number().required()
}),
}),
],
providers: [AppConfigService],
exports: [AppConfigService],
})
export class AppConfigModule { }
app.config.ts
import { registerAs } from '#nestjs/config';
export default registerAs('app', () => ({
env: process.env.NODE_ENV,
port: process.env.APP_PORT || 3000
...
}));
invoice.service.spec.ts
describe('InvoiceService', () => {
let service: InvoiceService;
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
imports: [
ConfigModule.forRoot({
load: [configuration],
ignoreEnvFile: true,
}),
AppConfigModule
],
providers: [
InvoiceService,
....
],
}).compile();
service = module.get<InvoiceService>(InvoiceService);
});
it('should be defined', () => {
expect(service).toBeDefined();
});
});
I use env file to deploy locally, and I only set up ignoreEnvFile: true in test class because env file is ignore from github repo, and project integrate github actions that run unit test.
How is the best way to solved this problem? I would not like add env file to repo.
Exist any way to disable/fake/mock Joi validation method. I saw some examples using setupFiles but I'm not sure if it's a good practice.
May you please show the package.json file?
you must have installed npm i --save #nestjs/config then create .env file inside root directory. Moreover you should import that into you app module.
import { Module } from '#nestjs/common';
import { ConfigModule } from '#nestjs/config';
#Module({
imports: [
ConfigModule.forRoot({
envFilePath: ".development.env",
}),
],
})
export class AppModule {}
Ref: https://docs.nestjs.com/techniques/configuration

Nestjs - connect bull-board in a normal controller

I created an app using nest.js and bull.
I added bull-board package to monitor my queues, but in documentation, only one way to add it to the app is mount as middleware:
In main.ts:
app.use('/admin/queues', bullUI);
Is there any way to add bullUI in a normal nest controller, after jwt auth? Like:
#UseGuards(JwtAuthGuard)
#Get("queues")
activate() {
return UI
}
You can use any express middleware like this inside controllers, but maybe some cases cause errors like serving static files with Guard exception and etc.
#UseGuards(JwtAuthGuard)
#Get("queues/*")
activate(#Req() req, #Res() res) {
bullUI(req, res)
}
I've got this working via a middleware consumer, so something like this:
import { router } from 'bull-board';
#Module({
imports: [
NestBullModule.forRoot({ redis }),
],
providers: [],
})
export class BullModule {
configure(consumer: MiddlewareConsumer): void {
consumer
.apply(router)
.forRoutes('/admin/queues');
}
}
I'd like to extend the original answer of #JCF, mainly, because it's working and much easier to understand.
I am using not default bull, with #nestjs/queue, but an improved version of BullMQ from anchan828 repo, with NestJS decorators, but I guess in both cases, the result will be the same.
The queue.module file:
#Module({
imports: [
BullModule.forRoot({
options: {
connection: {
host: redisConfig.host,
port: redisConfig.port,
},
},
}),
/** DI all your queues and Redis connection */
BullModule.registerQueue('yourQueueName'),
],
controllers: [],
providers: [],
})
export class QueueModule {
constructor (
#BullQueueInject('yourQueueName')
private readonly queueOne: Queue,
) {
/** Add queues with adapter, one-by-one */
setQueues([new BullMQAdapter(this.queueOne, { readOnlyMode: false })])
}
configure(consumer: MiddlewareConsumer): void {
consumer
.apply(router)
.forRoutes('/admin/queues');
}
}
Then just add it, to parent AppModule, via import, like that:
I am not sure, that Redis connection is needed here, for parent AppModule
#Module({
imports: [
BullModule.forRoot({
options: {
connection: {
host: redisConfig.host,
port: redisConfig.port,
},
},
}),
QueueModule
],
controllers: [],
providers: [],
})
export class AppModule {}
run the main.js, and visit, localhost:8000/admin/queues

ngx-socket-io Package is not work in angular JS

I was try use ngx-socket-io iPackage s not work in anguler JS
https://www.npmjs.com/package/ngx-socket-io Example But Not work.
let know any one if any other package to use in angular js.
npm install ngx-socket-io
Moddule.js
import { SocketIoModule, SocketIoConfig } from 'ngx-socket-io';
const config: SocketIoConfig = { url: 'http://localhost:8988', options: {} };
#NgModule({
declarations: [
AppComponent
],
imports: [
BrowserModule,
SocketIoModule.forRoot(config)
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
ChatService.js
import { Injectable } from '#angular/core
import { Socket } from 'ngx-socket-io'
#Injectable()
export class ChatService {
constructor(private socket: Socket) { }
assign_biker(biker_id){
this.socket.emit("assign_biker_order",{'biker_id:biker_id })
}
}
Do you Try socket client angular package ?
it so easy to use and worked in angular js.
Install first socket.io-client.
npm i socket.io-client
import * as io from 'socket.io-client';
private socket;
constructor() {
this.socket = io('here is your socket url');
}
assign_biker(biker_id){
this.socket.emit("assign_biker_order",{'biker_id:biker_id })
}

Resources