#Angular SSG - custom server?

17 messages · Page 1 of 1 (latest)

pseudo hull
#

Hi all, I have a question about Angular SSG. Can anyone point me in the right direction?
I want to make the step to SSG with our NX/Angular 19 applications. SSG is easy to implement and works fine. We use a design system based on StencilJS components, so I need to add logic to hydrate those components server side. It seems that Angular does not use the generated server.ts (express server) but directly the server.main.ts that bootstraps the Angular application with a serverside config.

Is there a way to add back custom server side rendering logic?

My expectations is that I would be able to provide my own server logic that is used by Angular to pre-render / SSG the pages. Specifically I want to add this logic from the StencilJS documentation. I can't find anything in the Angular documentation about editing the server rendering logic.

sand oracle
#

Angular does use server.ts in production always. But the dev server (ng serve) doesn't use it unless you are using the new AngularNodeAppEngine, which is currently in developer preview. This new API allows you to configure custom server routes and other custom server logic in a way that the dev server (which is not express, it uses Vite) can use.
See here: https://angular.dev/guide/hybrid-rendering#configuring-a-nodejs-server

pseudo hull
# sand oracle Angular does use `server.ts` in production _always_. But the dev server (`ng ser...

I guess you are refering to SSR, which I understand. I uses the 'real' server in prod, the dev one in dev. But when pre-rendering in build-time, both env do not hit the server.ts file. In my generated server.ts file there is this export section which is probably the reason why SSR uses the file, but SSG does not. But I cannot find anything about how it should work in my use case.

...express server logic 


// 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 default bootstrap;

sand oracle
#

Ah, I am not sure exactly about prerendering. Sorry, I missed that part. I'll have to do some digging

pseudo hull
#

Main logic from server.ts

// The Express app is exported so that it can be used by serverless Functions.
export function app(): express.Express {
  const server = express();
  const distFolder = join(process.cwd(), 'dist/apps/hosts/webshop/browser');
  const indexHtml = existsSync(join(distFolder, 'index.original.html'))
    ? join(distFolder, 'index.original.html')
    : join(distFolder, 'index.html');

  const commonEngine = new CommonEngine();

  server.set('view engine', 'html');
  server.set('views', distFolder);

  // Example Express Rest API endpoints
  // server.get('/api/**', (req, res) => { });
  // Serve static files from /browser
  server.get(
    '*.*',
    express.static(distFolder, {
      maxAge: '1y',
    }),
  );

  // All regular routes use the Angular engine
  server.get('*', async (req, res, next) => {
    const { protocol, originalUrl, baseUrl, headers } = req;

    commonEngine
      .render({
        bootstrap,
        documentFilePath: indexHtml,
        url: `${protocol}://${headers.host}${originalUrl}`,
        publicPath: distFolder,
        providers: [{ provide: APP_BASE_HREF, useValue: baseUrl }],
      })
      .then((html: any) => createWindowFromHtml(html, Math.random().toString()))
      .then((window: any) => hydrateDocument(window.document))
      .then((html: any) => res.send('poep'))
      .catch((err: any) => next(err));
  });

  return server;
}
lean mirage
#

you're still using the old engine though

sand oracle
#

You're using CommonEngine - that won't work.

lean mirage
#

i def get logs from my custom server logic during the prerender step with the new engine

pseudo hull
#

ah, might be some weird generation by NX then.

lean mirage
#

i'm not using any SSG pages though, so idk to what extend it's used

pseudo hull
#

But even though it's the old engine, it does not hit that endpoint

sand oracle
#

Correct. The "old engine" is only used during actual server side rendering in production.

#

If you want server.ts to be used in dev (or I am guessing for SSG - but I do not know that for sure) you have to switch (see my link above).

pseudo hull
#
 "server": {
      "dependsOn": ["build"],
      "executor": "@angular-devkit/build-angular:server",
      "options": {
        "main": "apps/hosts/webshop/src/server.ts",
        "outputPath": "dist/apps/hosts/webshop/server",
        "tsConfig": "apps/hosts/webshop/tsconfig.server.json"
      },
      "configurations": {
        "production": {
          "outputHashing": "media",
          "buildTarget": "webshop:build:nl:production"
        },
        "development": {},
        "nl": {
          "outputPath": "dist/apps/hosts/webshop/server",
          "localize": ["nl"]
        }
      },
      "defaultConfiguration": "production"
    },
   "prerender": {
      "executor": "@angular-devkit/build-angular:prerender",
      "options": {
        "routes": ["/test"],
        "discoverRoutes": false
      },
      "configurations": {
        "development": {
          "browserTarget": "webshop:build:nl:development",
          "serverTarget": "webshop:server:nl:development"
        },
        "production": {
          "browserTarget": "webshop:build:nl:production",
          "serverTarget": "webshop:server:nl:production"
        }
      },
      "defaultConfiguration": "production"
    }
sand oracle
#

You need to set the server property, outputMode and prerender.
Although I have never seen the prerender configuration extracted out like that.

lean mirage
#

don't recall where i got it from