How To Redirect All Routes To Index.html (angular) In Nest.js?
Solution 1:
You can only use setBaseViewsDir
and @Render()
with a view engine like handlebars (hbs); for serving static files (Angular), however, you can only use useStaticAssets
and response.sendFile
.
To serve index.html
from all other routes you have a couple of possibilities:
A) Middleware
You can create a middleware that does the redirect, see this article:
@Middleware()
exportclassFrontendMiddlewareimplementsNestMiddleware {
resolve(...args: any[]): ExpressMiddleware {
return(req, res, next) => {
res.sendFile(path.resolve('../frontend/dist/my-app/index.html')));
};
}
}
and then register the middleware for all routes:
exportclassApplicationModuleimplementsNestModule {
configure(consumer: MiddlewaresConsumer): void {
consumer.apply(FrontendMiddleware).forRoutes(
{
path: '/**', // For all routesmethod: RequestMethod.ALL, // For all methods
},
);
}
}
B) Global Error Filter
You can redirect all NotFoundExceptions
to your index.html
:
@Catch(NotFoundException)
export classNotFoundExceptionFilterimplementsExceptionFilter {
catch(exception: HttpException, host: ArgumentsHost) {
constctx= host.switchToHttp();
constresponse= ctx.getResponse();
response.sendFile(path.resolve('../frontend/dist/my-app/index.html')));
}
}
and then register it as a global filter in your main.ts
:
app.useGlobalFilters(newNotFoundExceptionFilter());
Solution 2:
Updated Answer for December 10, 2019
You need to create middleware for sending the react index.html
Create middleware file
frontend.middleware.ts
import { NestMiddleware, Injectable } from'@nestjs/common';
import {Request, Response} from"express"import { resolve } from'path';
@Injectable()
exportclassFrontendMiddlewareimplementsNestMiddleware {
use(req: Request, res: Response, next: Function) {
res.sendFile(resolve('../../react/build/index.html'));
}
}
Include middleware in
app.module.ts
import { FrontendMiddleware } from'./frontend.middleware';
import {
Module,
MiddlewareConsumer,
RequestMethod,
} from'@nestjs/common';
import { AppController } from'./app.controller';
import { AppService } from'./app.service';
@Module({
imports: [],
controllers: [AppController],
providers: [AppService],
})
exportclassAppModule {
configure(frontEnd: MiddlewareConsumer) {
frontEnd.apply(FrontendMiddleware).forRoutes({
path: '/**', // For all routesmethod: RequestMethod.ALL, // For all methods
});
}
}
App Structure for Reference:
Solution 3:
You can also use Cloud Functions for Firebase together with Firebase Hosting. What you have in main.ts is perfectly fine, with this approach you even don't need a controller. You should go as follows:
- Rename
index.html
toindex2.html
. This is important to render your route path, otherwise you will have rendering working fine on all routes, excluding the root/
. - Update
angular.json
to have the following"index": "apps/myapp/src/index2.html",
(Simply changeindex.html
toindex2.html
). Note: path to the index.html might be different for you, I'm using Nx workspace. - Add
templatePath: join(BROWSER_DIR, 'index2.html'),
to NestJS'sApplicationModule
, most probably you name the file as app.module.ts in a server directory.
Like so:
@Module({
imports: [
AngularUniversalModule.forRoot({
bundle: require('./path/to/server/main'), // Bundle is created dynamically during build process.liveReload: true,
templatePath: join(BROWSER_DIR, 'index2.html'),
viewsPath: BROWSER_DIR
})
]
})
Initialize Firebase Cloud Functions and Firebase Hosting, for how to set up this you can check https://hackernoon.com/deploying-angular-universal-v6-with-firebase-c86381ddd445 or https://blog.angularindepth.com/angular-5-universal-firebase-4c85a7d00862
Edit your firebase.json.
It should look like that, or at least the hosting
part.
{"hosting":{"ignore":["firebase.json","**/.*","**/node_modules/**"],"public":"functions/dist/apps/path/to/browser","rewrites":[{"function":"angularUniversalFunction","source":"**"}]}}
- In your main.ts you need to set up Cloud Functions on your server.
In a minimialistic case it would like something like that:
import * as admin from'firebase-admin';
import * as functions from'firebase-functions';
admin.initializeApp(); // Initialize Firebase SDK.constexpressApp: Express = express(); // Create Express instance.// Create and init NestJS application based on Express instance.
(async () => {
const nestApp = awaitNestFactory.create<NestExpressApplication>(
ApplicationModule,
newExpressAdapter(expressApp)
);
nestApp.init();
})().catch(err =>console.error(err));
// Firebase Cloud Function for Server Side Rendering (SSR).exports.angularUniversalFunction = functions.https.onRequest(expressApp);
With this approach you don't have to care about routes on the NestJS side. You can set up everything on the Angular side, and that's all. Angular takes care for routing. As you probably noticed this is Server-Side Rendering (SSR), but redirection of all routes to index.html
(or more precisely index2.html
) can be done using NestJS + Cloud Functions for Firebase in conjuction. Plus you have a SSR "for free" :)
Projects to showcase:
1) Angular + Angular Universal (SSR) + Cloud Functions for Firebase: https://github.com/Ismaestro/angular8-example-app (missing NestJS).
2) Angular + NestJS: https://github.com/kamilmysliwiec/universal-nest (missing Cloud Functions for Firebase).
Post a Comment for "How To Redirect All Routes To Index.html (angular) In Nest.js?"