Skip to content

Instantly share code, notes, and snippets.

@kgajera
Created May 25, 2020 22:44
Show Gist options
  • Star 5 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save kgajera/ec250b63efeae833a672dc281cc8af7b to your computer and use it in GitHub Desktop.
Save kgajera/ec250b63efeae833a672dc281cc8af7b to your computer and use it in GitHub Desktop.
Angular Express Server with Redis Caching
import 'zone.js/dist/zone-node';
import { APP_BASE_HREF } from '@angular/common';
import { ngExpressEngine } from '@nguniversal/express-engine';
import * as express from 'express';
import { existsSync } from 'fs';
import { join } from 'path';
import * as redis from 'redis';
import { AppServerModule } from './src/main.server';
// The Express app is exported so that it can be used by serverless Functions.
export function app(): express.Application {
const server = express();
const distFolder = join(process.cwd(), 'dist/browser');
const indexHtml = existsSync(join(distFolder, 'index.original.html'))
? 'index.original.html'
: 'index';
// Redis cache client
const redisClient = redis.createClient();
// Creates a cache key using the request URL
const cacheKey: (req: express.Request) => string = (req) =>
`ssr_${req.originalUrl}`;
// Universal express-engine
server.engine(
'html',
ngExpressEngine({
bootstrap: AppServerModule,
})
);
server.set('view engine', 'html');
server.set('views', distFolder);
// Middleware to send a cached response if one exists
const cachedResponse: express.RequestHandler = (req, res, next) =>
redisClient.get(cacheKey(req), (error: Error, reply: string) => {
if (reply?.length) {
// Cache exists. Send the response.
res.send(reply);
} else {
// Use the Universal engine to render a response.
next();
}
});
// Middleware to render a response using the Universal engine
const universalRenderer: express.RequestHandler = (req, res) => {
res.render(
indexHtml,
{
req,
providers: [{ provide: APP_BASE_HREF, useValue: req.baseUrl }],
},
(error: Error, html: string) => {
if (error) {
return req.next(error);
}
if (res.statusCode === 200) {
// Cache the rendered HTML
redisClient.set(cacheKey(req), html);
}
res.send(html);
}
);
};
// Serve static files from dist/browser
server.get(
'*.*',
express.static(distFolder, {
maxAge: '1y',
})
);
// All regular routes use the Universal engine
server.get('*', cachedResponse, universalRenderer);
return server;
}
function run() {
const port = process.env.PORT || 4000;
// Start up the Node server
const server = app();
server.listen(port, () => {
console.log(`Node Express server listening on http://localhost:${port}`);
});
}
// Webpack will replace 'require' with '__webpack_require__'
// '__non_webpack_require__' is a proxy to Node 'require'
// The below code is to ensure that the server is run only when not requiring the bundle.
declare const __non_webpack_require__: NodeRequire;
const mainModule = __non_webpack_require__.main;
const moduleFilename = mainModule && mainModule.filename || '';
if (moduleFilename === __filename || moduleFilename.includes('iisnode')) {
run();
}
export * from './src/main.server';
@mkoert
Copy link

mkoert commented Dec 16, 2020

How are you handling if redis cant be connected to or there is some other issue with redis?

@hiepxanh
Copy link

hiepxanh commented Feb 23, 2022

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment