I can't make my 404 page using Angular SSR deployed on Vercel Serveless Function
When accessing a bad url supposed to be 404 have this error This Serverless Function has crashed.
its a 500 internal server error
I am new to vercel and new to angular SSR so its hard for me to figure out how it is supposed to work, before using a serverless function I was trying to make my 404 work and it was also not working it was just doing the vercel 404 page instead of redirecting to my angular /404 it felt like my server.ts coudnt handle 404 and give it to my angular app and was throwing even before my app knew about it so my angular app routing could never do the job of redirecting to the correct /404 or just display the 404 component
It works locally ng serve (logic because its not using server.ts)
It also works doing node dist/project/server/server.mjs
(after a build using server.ts)
Here is my server.ts is angular standard
import { APP_BASE_HREF } from '@angular/common';
import { CommonEngine } from '@angular/ssr';
import express from 'express';
import { fileURLToPath } from 'node:url';
import { dirname, join, resolve } from 'node:path';
import bootstrap from './src/main.server';
// The Express app is exported so that it can be used by serverless Functions.
export function app(): express.Express {
const server = express();
const serverDistFolder = dirname(fileURLToPath(import.meta.url));
const browserDistFolder = resolve(serverDistFolder, '../browser');
const indexHtml = join(serverDistFolder, 'index.server.html');
const commonEngine = new CommonEngine();
server.set('view engine', 'html');
server.set('views', browserDistFolder);
// Example Express Rest API endpoints
// server.get('/api/**', (req, res) => { });
// Serve static files from /browser
server.get(
'**',
express.static(browserDistFolder, {
maxAge: '1y',
index: 'index.html',
}),
);
// All regular routes use the Angular engine
server.get('**', (req, res, next) => {
const { protocol, originalUrl, baseUrl, headers } = req;
commonEngine
.render({
bootstrap,
documentFilePath: indexHtml,
url: `${protocol}://${headers.host}${originalUrl}`,
publicPath: browserDistFolder,
providers: [{ provide: APP_BASE_HREF, useValue: baseUrl }],
})
.then((html) => res.send(html))
.catch((err) => next(err));
});
return server;
}
function run(): void {
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}`);
});
}
run();
My vercel deployment is pretty straightforward
{
"version": 2,
"public": true,
"name": "project",
"devCommand": "node dist/project/server/server.mjs",
"rewrites": [
{ "source": "/(.*)", "destination": "/api" }
],
"functions": {
"api/index.js": {
"includeFiles": "dist/project/**"
}
}
}
I have a folder api/ with index.js
const server = import('../dist/project/server/server.mjs');
module.exports = server.app;
My app.routes.ts are setup correctly
export const routes: Routes = [
{ path: '', redirectTo: '/home', pathMatch: 'full' },
{ path: 'home', component: HomeComponent },
{ path: '404', component: PageNotFoundComponent }, // I have this because I tried doing a redirectTo: '/404' but it didnt change anything
{ path: '**', component: PageNotFoundComponent },
];
Here is the runtime error:
TypeError: Cannot read properties of undefined (reading 'default')
at c (/opt/rust/nodejs.js:8:13746)
at /opt/rust/nodejs.js:8:13963
at new Promise (<anonymous>)
at Pt (/opt/rust/nodejs.js:8:13037)
at nn (/opt/rust/nodejs.js:9:78)
at Object.<anonymous> (/opt/rust/nodejs.js:9:478)
at Module._compile (node:internal/modules/cjs/loader:1358:14)
at Module._extensions..js (node:internal/modules/cjs/loader:1416:10)
at Module.load (node:internal/modules/cjs/loader:1208:32)
at Module._load (node:internal/modules/cjs/loader:1024:12)
Node.js process exited with exit status: 1. The logs above can help with debugging the issue.
INIT_REPORT Init Duration: 174.56 ms Phase: invoke Status: error Error Type: Runtime.ExitError
I made it work.....
api/index.js
export default import('../dist/project/server/server.mjs')
.then(module => module.app());
vercel.json
{
"version": 2,
"public": true,
"name": "project",
"rewrites": [
{
"source": "/(.*)",
"destination": "/api"
}
],
"functions": {
"api/index.js": {
"includeFiles": "dist/project/**"
}
}
}