Background: Using Angular Universal to perform pre-rendering but not all routes will be rendered (query parametered or authenticated-only pages for the most part), so wanting to fallback to the express renderer as needed.
Quick Replication (bash):
npm install -g @angular/cli@next
ng new partial-prerender -s -t --minimal --routing --interactive=false
cd partial-prerender/
ng add @nguniversal/express-engine@'^9.0.0-rc.1'
ng g m child --route child --module app
cat << 'EOF' > src/app/app.component.ts
import { Component } from '@angular/core';
@Component({
template: `<div [routerLink]="['/']">Root</div><div [routerLink]="['child']">Child</div><router-outlet></router-outlet>`,
})
export class AppComponent {}
EOF
npm run prerender
npm run serve:ssr
This quick replication will produce the app, universal implementation, a child page, and replace the app html to give 2 links and a router outlet, then build/pre-render. Both routes will be pre-rendered, but this is good enough for discussing the issue.
Problem: Dynamic SSR is performed as the Express server will pick up the request rather than serving the pre-rendered static file. URLs are normally accessed without the /index.html specified.
Note the static files can be found at /dist/partial-prerender/browser/index.html and .../child/index.html. For testing, I've replaced the contents of these files with garbage, just to be sure which is being loaded at a glance.
Can also add a console.log('DYNAMIC'); to the server.ts:
server.get('*', (req, res) => {
console.log('DYNAMIC');
res.render(indexHtml, { req, providers: [{ provide: APP_BASE_HREF, useValue: req.baseUrl }] });
});
When making a request to localhost:4000 or localhost:4000/child, the 'DYNAMIC' will be printed and the dynamically rendered version is produced, not giving me my mangled pre-rendered files.
When making a request to localhost:4000/index.html or localhost:4000/child/index.html, the
server.get('*.*', express.static(distFolder, { maxAge: '1y' }));
picks up and serves the mangled files.
All makes sense and why it's happening, but I want to be able to just hit a given url (without the /index.html and receive the pre-rendered files (when available), then fall back to putting SSR to work.
Potential Solution:
Modify server.ts to test for file existence matching the given request path + /index.html and serve them, falling back to the res.render(...
express.static should be utilized in some way over fsfs is the answer, would it make sense to cache the pre-rendered files in memory?If it helps, I produce an alpine-node container and deploy to K8s with an Nginx ingress. Only mention this as maybe there's a magical try-files-like functionality that can be done to 'attempt' a file + /index.html retrieval from the node container, then fallback without the /index.html, but seems highly unlikely.
You could use an if statement inside the get request like this
const fullPath = join(distFolder, req.originalUrl);
if (existsSync(fullPath)) {
console.log('STATIC Exists');
return res.sendFile(join(distFolder, req.originalUrl));
} else {
//Dynamic
res.render(indexHtml, { req, providers: [{ provide: APP_BASE_HREF, useValue: req.baseUrl }] });
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With