nestjs+nunjucks how to render async template - nestjs

I created a layout, and the layout header nav menus data from an api service.
how can I get api service before render the layout.
the layout is includes by every view page

With ServeStaticModule you can server every view page
import { Module } from '#nestjs/common';
import { ServeStaticModule } from '#nestjs/serve-static';
import { join } from 'path';
import { AppController } from './app.controller';
#Module({
imports: [
ServeStaticModule.forRoot({
rootPath: join(__dirname, '..', 'client'),
exclude: ['/api*'],
}),
],
controllers: [AppController],
})
export class AppModule {}
I hope it serves you.
View sample full here https://github.com/yordanisperez/nest/tree/master/sample/24-serve-static

Related

Nest can't resolve dependencies of the SequelizeCoreModule

I'm trying to import SequelizeModule in my app.module.ts but I got the following error:
[Nest] ERROR [ExceptionHandler] Nest can't resolve dependencies of the
SequelizeCoreModule (SequelizeModuleOptions, ?). Please make sure that
the argument ModuleRef at index [1] is available in the
SequelizeCoreModule context.
app.module.ts
import { Module } from '#nestjs/common';
import { ModuleRef } from '#nestjs/core';
import { SequelizeModule } from '#nestjs/sequelize';
import { join } from 'path';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { TweetsController } from './tweets/tweets.controller';
import { TweetsModule } from './tweets/tweets.module';
import { TweetsService } from './tweets/tweets.service';
#Module({
imports: [
SequelizeModule.forRoot({
dialect: 'sqlite',
autoLoadModels: true,
synchronize: true,
host: join(__dirname, 'database.sqlite'),
}),
TweetsModule,
],
controllers: [AppController, TweetsController],
providers: [AppService, TweetsService],
})
export class AppModule {}
this happens when you have multiple nodejs modules loaded for the same #nestjs/core package. See them by running npm ls #nestjs/core. You can solve that by getting ride of those packages somehow and keeping only the one that your app depends on directly. Read the docs: https://docs.nestjs.com/faq/common-errors#cannot-resolve-dependency-error

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

exporting nest js middleware to another project

I'm trying to export a custom middleware project in NestJS to all my other projects(and import this in all of them). My actual class is acl-jwt.middleware.ts in its bootstrap src folder.
In the acl-jwt.middleware.ts I have:
import { Injectable, NestMiddleware, } from '#nestjs/common';
#Injectable()
export class AclJwtMiddleware implements NestMiddleware {
use(req: any, res: any, next: () => void) {
console.log("ACL JWT Middleware !!")
next();
}
}
and my app.module.ts has:
import { Module, NestModule, MiddlewareConsumer, RequestMethod } from '#nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { AclJwtMiddleware } from './acl-jwt.middleware';
#Module({
imports: [],
controllers: [AppController],
providers: [AppService],
})
export class AppModule implements NestModule {
configure(consumer: MiddlewareConsumer) {
consumer.apply(AclJwtMiddleware).forRoutes({ path: '*', method: RequestMethod.ALL });
}
}
And in my another project's app.module.ts, I'm importing this like the following
import { AclJwtMiddleware } from 'mi';
#Module({
imports: [],
controllers: [AppController],
providers: [AppService],
})
export class AppModule implements NestModule {
configure(consumer: MiddlewareConsumer) {
consumer.apply(AclJwtMiddleware).forRoutes({ path: '*', method: RequestMethod.ALL });
}
}
mi is my hosted package.
And then when I run my second project, I get the following error:
src/app.module.ts:16:34 - error TS2307: Cannot find module 'mi' or its corresponding type declarations.
16 import { AclJwtMiddleware } from 'mi';
Am I not exporting something? Or am I not importing it properly?
I've been searching all over the internet but I couldn't find something with my use case. Any help would be really good. Thank you all! :)
You'd need to make sure that in your package.json of your mi package, you have either main or files that points to the properly exported file/directory. If it is a barrel file, like index.js, then you need to make sure it exports the middleware properly.

TransferHttpCacheModule doesn't prevent duplicate HTTP Calls in Angular 8

I've set up an Angular 8 Project with Angular Universal. To prevent duplicate HTTP Calls, Angular offers TransferHttpCacheModule.
I followed the official Documentation to add TransferHttpCacheModule to Angular (https://github.com/angular/universal/blob/master/docs/transfer-http.md)
I also tried to add the BrowserTransferStateModule (https://www.twilio.com/blog/faster-javascript-web-apps-angular-universal-transferstate-api-watchdog), but this doesn't work either.
app.module.ts
import {BrowserModule} from '#angular/platform-browser';
import {AppRoutingModule} from './app-routing.module';
import {AppComponent} from './app.component';
import {TransferHttpCacheModule} from "#nguniversal/common";
#NgModule({
declarations: [
AppComponent,
...
],
imports: [
BrowserModule.withServerTransition({appId: 'serverApp'}),
TransferHttpCacheModule, // <-
...
HttpClientModule
]
bootstrap: [AppComponent]
})
export class AppModule {
}
app.server.module.ts
import {NgModule} from '#angular/core';
import {ServerModule, ServerTransferStateModule} from '#angular/platform-server';
import {AppModule} from './app.module';
import {AppComponent} from './app.component';
import {ModuleMapLoaderModule} from '#nguniversal/module-map-ngfactory-loader';
#NgModule({
imports: [
AppModule,
ServerModule,
ModuleMapLoaderModule,
ServerTransferStateModule // <-
],
bootstrap: [AppComponent],
})
export class AppServerModule {
}
main.ts
document.addEventListener('DOMContentLoaded', () => {
platformBrowserDynamic()
.bootstrapModule(AppModule)
.catch(err => console.error(err));
});
My Api Service
api.service.ts
import {Injectable} from '#angular/core';
import {HttpClient} from '#angular/common/http';
import {Observable} from "rxjs";
#Injectable({
providedIn: 'root'
})
export class ApiService {
private API_URL = '/api/';
constructor(private http: HttpClient) {
}
public get<T>(url: string): Observable<T> {
return this.http.get<T>(this.API_URL + url);
}
public post<T>(url: string, payload: T): Observable<T> {
return this.http.post<T>(this.API_URL + url, payload);
}
...
}
HTTP Call
home.component.ts
import {Component, OnInit} from '#angular/core';
import {ApiService} from "../../api.service";
import {Offer} from "../../offer-preview/offer.model";
#Component({
selector: 'app-home',
templateUrl: './home.component.html',
styleUrls: ['./home.component.sass']
})
export class HomeOffersComponent implements OnInit {
latestOffers: Offer[];
constructor(private apiService: ApiService) {
}
ngOnInit() {
this.apiService.get<Offer[]>("offer")
.subscribe(data => this.latestOffers = data);
}
}
According to the official TransferHttpCacheModule Docs, the Browser should not make an XHR to /api/offer, but the i see the XHR Call in the Developer Network Tools.
What am i doing wrong? Did I miss anything?
This is because of a mismatch between the absolute URLs the TransferHttpCacheModule is using as cache key for each request.
For example:
Server
Client
baseUrl
localhost:4200
domain.com
ressource
/api/people
/api/people
Absolute URL
http://localhost:4200/api/people
https://example.com/api/people
Only if both Absolute URLs are the same the request is successfully cached.
To circumvent this shortcoming you can implement your own caching via the BrowserTransferStateModule and Interceptors, e.g. as described here.
Just be sure to only use the relative URL:
const url = new URL(req.url);
const rel = url.toString().substring(url.origin.length);
this.transferState.set(makeStateKey(rel), event.body);

Change hash-url to hash-bang url using angular4

I am using angulr4 this is my URL http://localhost/#/login.html I want to change this url to http://localhost/#!/login.html. I found solution for angularjs but not for angular4. I am using "Prerender Node" for SEO regarding this is link https://www.npmjs.com/package/prerender-node
Thanks in advance
What you are after is APP_BASE_HREF. In your routing module app-routing.module.ts add to your module providers array { provide: APP_BASE_HREF, useValue: '!' } and import import { APP_BASE_HREF } from '#angular/common'; at the top of the file. It looks like you are already using HashLocationStrategy.
Example app-routing.module.ts:
import { NgModule } from '#angular/core';
import { Routes, RouterModule} from '#angular/router';
import { APP_BASE_HREF } from '#angular/common';
const routes: Routes = [
{ path: '**', redirectTo: '404-not-found' }
];
#NgModule({
imports: [
RouterModule.forRoot(routes, {
useHash: true
})
],
exports: [ RouterModule ],
providers: [
{ provide: APP_BASE_HREF, useValue: '!' }
]
})
export class AppRoutingModule {}
Further reading Angular 4 documentation
PathLocationStrategy:
PathLocationStrategy is a LocationStrategy used to configure the
Location service to represent its state in the path of the browser's
URL.
If you're using PathLocationStrategy, you must provide a APP_BASE_HREF
or add a base element to the document. This URL prefix that will be
preserved when generating and recognizing URLs.
For instance, if you provide an APP_BASE_HREF of '/my/app' and call
location.go('/foo'), the browser's URL will become
example.com/my/app/foo.
Similarly, if you add <base href='/my/app'/> to the document and call
location.go('/foo'), the browser's URL will become
example.com/my/app/foo.
HashLocationStrategy:
You can go old-school with the HashLocationStrategy by providing the
useHash: true in an object as the second argument of the
RouterModule.forRoot in the AppModule.
APP_BASE_HREF:
... APP_BASE_HREF token represents the base href to be used ...
a string representing the URL prefix that should be preserved when generating and recognizing URLs

Resources