angularhttp-status-code-404server-side-renderingvercelangular-ssr

Angular 18 SSR Vercel issue with 404


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

Solution

  • 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/**"
        }
      }
    }