angular / angular-cli

CLI tool for Angular
https://cli.angular.dev
MIT License
26.79k stars 11.98k forks source link

Hybrid rendering without any pre-rendered routes should not execute application bootstrapping #28921

Closed ManuelRauber closed 4 days ago

ManuelRauber commented 4 days ago

Command

build

Is this a regression?

The previous version in which this bug was not present was

No response

Description

For my use-case I use Angular 19 Hybrid SSR. For now, I only need the SSR part, not making use of any other RenderMode, but I want to keep the doors open for later.

During the bootstrap of the application I'm using an app initializer to get some data via HTTP. The website cannot run without it (think of getting different configurations from an API depending on the hostname), so it makes sense to get the data as soon as possible and assume the website always runs with a preloaded configuration.

When I use Angular SSR my builds works as expected. When I sure Angular Hybrid SSR my build hangs because it tries to make a request to the API which is not available on the build server.

The configuration for Hybrid SSR is:

// angular.json
"outputMode": "server"

// app.routes.server.ts
export const serverRoutes: ServerRoute[] = [
  {
    path: '**',
    renderMode: RenderMode.Server
  }
];

As you can see, there's no pre-rendering involved.

I want to use Hybrid SSR because it makes use of the same request handler during development where the normal SSR uses its own internal SSR middleware. Using the same handler seems more error-proof to me.

Minimal Reproduction

For both examples make sure to have Angular 19 installed.

Example with Angular SSR

  1. ng new v19ssr --ssr
  2. In app.config.ts add an app initializer: provideAppInitializer(() => console.log('Hello'))
  3. In angular.json change the existing prerender to false.
  4. Run ng build

Observe, that there is no Hello in your console. This is as it should be.

Example with Angular Hybrid SSR

  1. ng new v19ssrSR --ssr --server-routing
  2. In app.config.ts add an app initializer: provideAppInitializer(() => console.log('Hello'))
  3. In app.routes.server.ts switch the render mode to Server
  4. In angular.json change the outputMode to server
  5. Run ng build

Observe, that there is Hello in your console, so the whole app has been boostrapped even when there is no route to prerender.

Exception or Error

Your Environment

Angular CLI: 19.0.0
Node: 20.17.0
Package Manager: npm 10.8.2
OS: darwin arm64

Angular: 19.0.0
... animations, cli, common, compiler, compiler-cli, core, forms
... platform-browser, platform-browser-dynamic, platform-server
... router, ssr

Package                         Version
---------------------------------------------------------
@angular-devkit/architect       0.1900.0
@angular-devkit/build-angular   19.0.0
@angular-devkit/core            19.0.0
@angular-devkit/schematics      19.0.0
@schematics/angular             19.0.0
rxjs                            7.8.1
typescript                      5.6.3
zone.js                         0.15.0

Anything else relevant?

No response

alan-agius4 commented 4 days ago

This behavior is intended. The application will be bootstrapped during the build process to validate and extract all routes, generating an SSR manifest.

In the future, file-based routing may address this issue.

Currently, you can set the environment variable NG_BUILD_PARTIAL_SSR=1, but be aware that this will slow down the first request and may cause validation errors to appear at runtime instead of during the build process.

ManuelRauber commented 4 days ago

This behavior is intended. The application will be bootstrapped during the build process to validate and extract all routes, generating an SSR manifest.

In the future, file-based routing may address this issue.

Currently, you can set the environment variable NG_BUILD_PARTIAL_SSR=1, but be aware that this will slow down the first request and may cause validation errors to appear at runtime instead of during the build process.

Hm, interesting, since the behaviour is different to the non-hybrid SSR, but I understand why it is necessary at the moment.

For my use case, I think I can safely use a mock value when inject(REQUEST) is null, which will happen during build time since I don't have pre-generated routes.

Or is there any possibility to detect build-time explicitly?

alan-agius4 commented 4 days ago

Or is there any possibility to detect build-time explicitly?

No there isn't a way

ManuelRauber commented 4 days ago

Or is there any possibility to detect build-time explicitly?

No there isn't a way

I've checked using the mock object, but that's not working for me.

So, I need to use the usual SSR approach for now.

You can close this issue if you want, since the original behavior is intended and does not fit my use case. :-)