angular-cli: Providing a DI token in commonEngine.render() does not provide its value to application
Which @angular/* package(s) are the source of the bug?
Don’t known / other
Is this a regression?
Yes
Description
Prior to upgrading to v17 our application passed express’ req/res as REQUEST/RESPONSE DI Tokens through the render function of the engine in server.ts. Since upgrading to v17 from v16 these DI Token values are always null.
This can be seen in the minimal reproduction where all three of APP_BASE_HREF / REQUEST / RESPONSE are null in the console output while running ‘ng serve’.
If using @angular-devkit/build-angular:browser instead of @angular-devkit/build-angular:application this problem does not occur. I assume there is a different way to provide dependencies from server.ts now but I have not been able to find any updated documentation on it.
Please provide a link to a minimal reproduction of the bug
https://github.com/Ozzcer/angular-ssr-di-issue
Please provide the exception or error you saw
Console outputs null for all DI Tokens provided in server.ts
Please provide the environment you discovered this bug in (run ng version
)
Angular CLI: 17.0.0
Node: 18.17.1
Package Manager: npm 9.6.7
OS: linux x64
Angular: 17.0.2
... animations, common, compiler, compiler-cli, core, forms
... platform-browser, platform-browser-dynamic, platform-server
... router
Package Version
---------------------------------------------------------
@angular-devkit/architect 0.1700.0
@angular-devkit/build-angular 17.0.0
@angular-devkit/core 17.0.0
@angular-devkit/schematics 17.0.0
@angular/cli 17.0.0
@angular/ssr 17.0.0
@schematics/angular 17.0.0
rxjs 7.8.1
typescript 5.2.2
zone.js 0.14.2
Anything else?
No response
About this issue
- Original URL
- State: open
- Created 8 months ago
- Reactions: 102
- Comments: 89 (11 by maintainers)
Links to this issue
Commits related to this issue
- build: add Angular 17 support & update packages (#477) Hello @santoshyadavdev, This PR fixes https://github.com/uiuniversal/ngu-carousel/issues/476. What I did: - I couldn't use `yarn` at al... — committed to uiuniversal/ngu-carousel by Yberion 7 months ago
- Add SSR script and update server-side obj handling Added a new script entry in `package.json` to address the Server-Side Rendering (SSR) issue in the Angular project and updated the `AppComponent` to... — committed to germanfica/angular-ssr-di-issue by germanfica 3 months ago
- Fix SSR script and update server-side obj handling Added a new script entry in `package.json` to address the Server-Side Rendering (SSR) issue in the Angular project and updated the `AppComponent` to... — committed to germanfica/angular-ssr-di-issue by germanfica 3 months ago
- Add SSR script and update server-side obj handling Added a new script entry in `package.json` to address the Server-Side Rendering (SSR) issue in the Angular project and updated the `AppComponent` to... — committed to germanfica/angular-ssr-di-issue by germanfica 3 months ago
This is a known problem and we’re working on resolving it. Currently, the plan is to ship it as part of the v18 release this May. If anything unexpected pops up and we’re unable to deliver in this timeframe we’ll follow up here.
I agree, in development mode, we should be able to get the request object to do, for example, authentication through the cookie.
This is a breaking change between v16 and v17, and I don’t know if it was documented.
This is a serious issue IMO, we should be able to inject
REQUEST
andRESPONSE
withng serve
How we can debug our app then ?
By building and running server.mjs in dist folder ?!
So
server.ts
is used only for production ?? Or am I missing something ?? I will continue using oldssr-dev-server
for now(I remember that in v16 and below,
server.ts
was used for both development and production. Why shouldn’t it be the same as before? Or if we can useserver.ts
for both envs + esbuild, how can we do that?)How do they expect to work authentication with cookies without access to REQUEST RESPONSE from the server.ts?
Imagine we created a fresh new (SSR) project with latest version of
@angular/cli
and we want to make sure that angular ssr server returns404
HTTP Status Code instead of200
for unregistered routes or we want to redirect a path to another based on some conditions only, so we should set301
HTTP Status Code and … We definitely need serverresponse
object to set the right HTTP Status Code (and alsorequest
for other use cases)My question is: How we can do it in development environment ?!
Is the issue scheduled? Can you tell us approximately when we can expect a fix? This is a very important matter for us. Thank you.
This issue is a bummer also for 3rd party Angular library authors that depend on REQUEST/RESPONSE tokens. Now the library users that create a fresh Angular 17+ app (with application builder used by default), running SSR dev-server, face unexpected errors, because the injection tokens
REQUEST/RESPONSE
being not provided - becasueserver.ts
is not included in the SSR dev server run.Note: I’m aware that since ng17, REQUEST/RESPONSE injection tokens need to be provided by the application authors themselves in
server.ts
, it’s acceptable. What is disturbing the Developer Experience, is thatserver.ts
is not included at all when running the (Vite) SSR dev server, thereforeREQUEST/RESPONSE
tokens have no chance to work.I respect and admire all the work you, the Angular core team, are doing to help improve DX around SSR and performance!! 🙏 At the same time, I’m hoping we can avoid introducing an unintended regression to DX by excluding
server.ts
file from the SSR dev server run, if possible.Again, thanks for keeping this framework great and developing it even better!
it’s definitely a weird design choice where development and production code is forced to be different. are there any plans to change this behavior? debugging with npm run project/dist/server is hacky at best
Already several months now and still nothing. Give priority to solving the problem please
Thank you all for the additional details. Just to reiterate, this issue pertains specifically to
ng serve
.We’ll investigate this issue and ensure to keep you updated on any developments in this thread.
This is indeed very strange. It seems that the problem is very widespread, the latest version has become unusable on SSR without any mention during the update and there is no information. The problem seems to be a major inconvenience and despite the immense gratitude for what you do, it would be nice to hear even an approximate date when you will provide us with some new information, where we stand and what are the obstacles. The RFC + open Reactivity projects showed that you can be transparent in your communication and we as a community appreciate it very much. In the case of such a popular problem, we would like to know similarly where we stand. But of course, you are in charge here.
Greetings to the Angular team and don’t take this negatively. We just want to be able to use all the new things you give us 😃
In reference to my closed but related thread, I believe that the lack of use of server.ts in development mode is a very large regression. There are often things that we need to change there and be able to debug based on the RESPONSE and REQUEST objects. The configuration of this and the work on SSR has unnecessarily become more complicated. I hope that this will be somehow sensibly resolved because in its current form it is impossible for me to work with it on a daily basis and I am forced to use the classic approach in the Angular 16 version.
I almost lost my last brain cell trying to understand why nothing is being executed in server.ts, this need to be documented or at least mentioned while migrating to angular 17
I have ran into same issue (confusion). I generated fresh app using angular 17, with
--ssr
option. Everything looks nice,ng serve
was working, until I have a need to create new endpoint inserver.ts
. To my surprise my endpoint was not registered, and I was gettingERROR RuntimeError: NG04002: Cannot match any routes. URL Segment: 'api/user'
Initially I though I made a mistake and start trying to fix it, and after sometime of failure after failure, I start realizing maybeserver.ts
was not part ofng serve
?It was confusing and I waste time trying to fix it.
It was super unintuitive, I had fresh app, generated by CLI, I’ve run app using
ng serve
which used default (generated) configuration and to found out server entrypoint was not working 🤯Do we have any confirmation if the ng serve not using server.ts is a bug or a feature?
jokes aside
really, is it a known bug or its designed that way for a reason?
Ive had to stop developing in ssr yesterday because of this, there is no wey to get my Bearer Token form the cookies in the ssr first api call…
Any thing new on this issue? Upgraded to v17 this week and had to update my code to use standalone routing and
bootstrapApplication
etc so this is the only error left. Trying to use thebrowser
builder doesn’t seem to work for me either.I can treat it as optional because it’s not heavily used but then I won’t be able to test requests when
isPlatformServer is true
. Is this the intent going forward as a byproduct of using vite or something that will be added in a later version?Ah ok, got it working now, I have had to run build and run server.mjs independently. Is there a way to get the dev-ssr-server to work with the application output / is there an equivalent builder available for application output? Or do we still need to use dev-ssr-server with seperate server + browser builder instead of application? The current documentation is quite confusing as it appears like running the application builder through
ng serve
should allow local ssr renderingI’m digging internet for 12 hours and no solution, cookies coming from client not passing to api calls made by node
I’ve been maintaining a project for a while which has more things added to server.ts than just Express tokens:
The Angular app can run fine without them, but when work needs to be done on any of those, a developer must be able to run it locally, run tests, etc…
I wasted so much time because it was indeed rendered server side but yet the request provider was empty.
The changelog of the last version (adding node run dist/xxx) put me on the path to this issue.
So confusing behavior.
It has to be at least clearly documented on the getting started page…
I’ve been confused by this as well. I was hoping there is an alternative way for
ssr-dev-server
in theapplication
builder so that we can both develop the app, have it rebuild on the fly and just use easily.Since the
server.ts
is not used forng serve
, it also means that none of the custom API routes that are defined are registered. What is the recommended approach here?I’ve spent like 3 hours praying to god, how nothing from the server.ts is executed… then found this issue 😄 How they could make this feature stable with this bottleneck. What’s the progress?
A few months have passed and unfortunately, migrating to Angular version 17 is still not possible in our company, despite the fact that there is theoretically time for it. The version without SSR works great, but unfortunately we need SSR and rely on REQ and RES during development. It would be good if the creators at least gave a signal that they understand the problem and are looking for a solution or something. This lack of information is not optimistic.
I hope that you will quickly deal with this and allow us to use the amazing new features of Angular
Our team has to keep v16 because of this issue as well. Ignoring custom
server.ts
in dev mode is a huge downgrade for apps with complex SSR.One of the
request
use cases: (user authentication)Getting the cookie from browser request and add it to api request for protected pages like user profile (with interceptor) + allowing cookie header (angular/angular#15730) Now without
request
object we can’t do that in dev env (SSR)And imagine if we already have a guard for the routes !! we can’t enter the user profile …
@SaeedDev94 I agree, and what’s more, it’s not at all obvious. During the upgrade from version 16=>17, it is automatically migrated to devkit-angular:application, so despite the migration, you have to manually revert the changes. And using the old builder is problematic because nguniversal has stopped at version 16, which means we are forced to install it permanently with --force until this is fixed.
I’ve been closely following this discussion due to encountering the same challenge.
Upon reviewing the Angular documentation, particularly the esbuild page (https://angular.io/guide/esbuild), I noticed that it lists known issues but surprisingly omits this specific scenario. Given its critical impact on server-side rendering setups and Angular Universal implementations, including this information could be highly beneficial. It would help set proper expectations for other organizations facing similar upgrade decisions and provide much-needed clarity on the issue.
Acknowledging this challenge in the documentation and, ideally, providing a workaround or solution would greatly assist developers and companies like ours in planning our upgrade paths more effectively.
Thank you for all the hard work and dedication to improving Angular. It’s genuinely appreciated, and we look forward to overcoming this hurdle with your support.
By the way, you guys can upvote this feature request: https://github.com/angular/angular-cli/issues/27144
What about new developers and new projects that they don’t know there is a such workaround at all (old ssr engine) ?
+ The weak documentations about this topic?
+ 2 different envs for development and production?
+ The old engine is really slow compared to the new one
Though this patch seems to fix a concrete issue with cookies, I believe the root cause lies in fact that
application
builder is not a drop-in replacements for@angular-devkit/build-angular:ssr-dev-server
, because theapplication
builder does not run theserver.ts
but instead creates the in-memory one (not sure what’s the right term here) to do SSR.Not sure what’s the vision of the Angular team at this point, yet the drop-in replacement is what I’d personally expect.
I have a guard that passes the cookies in a callback, it is logging undefined in the back-end even though the cookie is existing in the client. I think my issue is related to this one.
@zygarios as long as you don’t migrate to the new builder, everything will work as it used to.
I’m just scared they’re going to remove the old builder in a future version, without providing a proper solution to this problem
Unfortunately this is also the case for us 🤔 Another things which I noticed 🤔
For example when you build in prod mode you move
assets
into dist. But running ssr in dev mode does not move them. Example use case: Our app preloads all material icons in memory so that we can add the svg icons to the registry and when ssr goes we render them without reading from disk we have them cached in a hasmap and we can inline them directly in the html file.We ship our environment variables hardcoded inside the index.html file 🤔 In order to do that get the build index where we have a special script tag and we inject the env vars there 🤔 maybe we are not supposed to do this but this enables 1 build to be deployed in any environment without rebuilding the app again.
We need access to index.html because we also replace the app bootstrapping scripts with a static script 🤔 this helps us to have for example smarter cloudfront cache invalidation when we release new versions. To achieve this we remove the inlined scripts from index and inject 1 script which has a static name which bootstraps the app, with this small change we can invalidate only that script on cloudfront and not everything inside the distribution and the new app is basically served to everyone without problems and if somebody somehow accesses the old app the scripts are still there nothing will crash.
I can achieve 2 by altering the index when dev server starts 🤔 but I think I cannot achieve 3 🤔 there what I do is when a request comes I run
ssr
I get the output -> remove the scripts from html and inject our bootstrap script which will calleval
for every js in this index.html so I don’t have a good idea if I can do this in the dev server now 🤔 as long as it works in production I don’t mind not doing this in the devserver but I need to know how I can support this use cases which we have.A week going nuts & wondering if I am a fraud. I can now rest in peace 😭 I have been a bit of a mess since I stumbled on this.
My use case: hide Firebase from my clients, so I can just switch without refactoring the frontend (Firebase does not support region/zone fallbacks, so good for demo only).
Additionally, I want automatic state transfer for my i18n keys through the HTTP client (not custom craft as is the case with Firebase and Sanity).
Either way, I need that express server during development.
It’s not a blocker to upgrade at all! But to ship this dev server as the default for new projects when it doesn’t support an incredibly common SSR use case without any documentation about that fact, and no alternative, is very silly.
If this was an opt-in beta-level feature, it would be far less egregious. Especially since it seems primarily like an oversight with no real technical justification.
The problem is not if the PROD mode is enabled or not. It is just, that a
ng serve
don’t use the server.ts.That looks correct to me - although any provider I try to create doesn’t get it’s data passed through when injected even in PROD mode (where server.ts is use) on the server side
Yes, I know, thank you. But this would lead to use 5 instead of 2 builders again and though not investigated, probably would have to work with modules again. But I like to stay as close as possible to the new variant, as the other builders probably will fall out anyway in near future. And if setting up a new project, I don’t like to back on an old horse. And the workaround is at least a good middle way, I guess.
Until v18 arrives, is there a way to inject Vite requests and read HTTP headers? In production mode, there is the possibility to inject an Express request and use it, for example, in an HTTP interceptor. However, I cannot find a way to do this in development mode with Vite. Thank you.
I
This method works perfect if you disabled prerender
in angular.json “prerender”: false
Huh, same here 😦
To get rid of some static environment profiles and to use configurations on server I tried following:
Simplified example:
Declare an injection token
than in server.ts
in a service or in app.component.ts
Is this a bug or are the providers not intended to be used for such purposes? Do I need to register the tokens passed to common engine elsewhere too?
Kind regards, Michael
ng serve
does allow SSR, but it does not use theserver.ts
, Instead it has it’s own middlewares and server instance. You should definitly update the docs to make this clear.Hi @JeanMeche , tokens are defined under express.tokens.ts in minimal reproduction (https://github.com/Ozzcer/angular-ssr-di-issue/blob/c08049cc39e2993eff903c29902826a1aa1f7d10/src/express.tokens.ts), this file was created automatically by ‘ng update’ in our original project and I copied the file over to the minimal reproduction.
Also APP_BASE_HREF is coming through as null and I thought that didn’t need to be manually defined.
Hey, @mgechev. Is there still a chance for SSR fix in version 18?
Sorry, my message was misunderstood, really the problem I show in my example is about the REQUEST and the RESPONSE . I updated my comment for better understanding:
@CarlosTorrecillas Thanks for the response, but I know I did the same thing as you.
I think the quickest path forward that would unblock a majority of people is injecting
Request
properly during the Vite builder SSR, as this is how most apps are going to be accessing cookies, and cookies are basically the only way to do any sort of authenticated SSR.Beyond that, even if full custom
server.ts
support isn’t feasible at this time, registering custom middlewares in some way would handle basically every other use case.Side note @alan-agius4 as you may have some insight here: I would work on a PR for this myself but I cannot run an clean
npm install
in the parent workspace of the CLI repo or thebuild_angular
package without either peer dep errors or a missingpackage.json
that the installer is trying to grab from the cache.I still don’t understand how I can achieve responsiveness without using REQUEST and the User-Agent contained therein. I have an application that uses hydration and has different components for the mobile view and others for the desktop view. However, without information about the device type on the server, I am forced to always serve the default view (mobile), which results in a strange layout change in the browser. Is there any other way to solve this than by using REQUEST?
@hudzenko You can still update to v17 and keep using the
browser
(webpack) orbrowser-eslint
builder.Any solution on that?
Summarizing all mentioned above, the following conclusion can be made.
Providing DI tokens for SSR does not work when building an application using
@angular-devkit/build-angular:dev-server
. This is due todev-server
not usingserver.ts
and Express itself, but using its own middleware and server. Also, this tokens will not be available when using theprerender
option.To solve this problem, the application needs to be built using
@angular-devkit/build-angular:application
and disable theprerender
option (or exclude routes that use the tokens).The following code can be applied:
The most convenient way to hot reload in a development environment that I’ve found is to build the application using
ng build --watch --configuration development
in conjunction withnodemon
.Hope this comment helps other developers to quickly find a solution to this problem, until the Angular team resolves this issue with the v18 release.
Is there any solution to return status code 404 in Angular 17 using @angular/ssr?
https://github.com/angular/angular-cli/pull/26667#pullrequestreview-1781435147 since this already merge, which allow index.html can manipulate, then we can add some thing like: server variable => index.html => angular read from this to client => then delete before we we send client if need. I think this is dirty hack for someone really need. I think it call “Embedding Variables in HTML”
I agree with all the others. Not using server.ts while running serve is pointless. Still I didn’t want to do without Angular 17, as there are a couple of new features, that are worth it. So I was looking at the way, how to get around that issue.
A workaround for now, as a hint for others, would be running build command with development configuration and in parallel
node dist/xxx/server.ts
. Only pitfall is, you have to reload the browser yourselve, but at least it’s close toserve
.Still hope, Angular team will have a look into this and I’m sure they find a solution.
at first i thought that much better : fast build time frontend and backend(ssr) all with one command
ng serve
, now that I understand that vite makes its own server XD which makes me wonder what the point of SSR if I can’t INTERACT WITH IT!!! (server.ts
). of course with all respect ❤️ AngularNo, it’s not possible for now. https://github.com/angular/angular-cli/issues/26323#issuecomment-1971390348
I did a quick proof-of-concept
patch-package
patch to test how far away this really is, and it’s just a handful of lines! Thereq
is right there: all we have to do is pass it to the render function and add it to the list of providers.Here’s a gist with my POC patch (using a plain string DI token as the actual
InjectionToken
token didn’t seem to resolve properly). Works like a charm, I just have to manually parse the cookies out as you can see, since my use case makes use of thecookie-parser
middleware. I’d file an actual PR, but as I mentioned before, the reponpm i --force
fails so doing actual contribution work beyond fixing typos is blocked.NOTE: If you are actually going to use this patch, you’ll need to coalesce between the actual
Request
token injected value and the value injected by this plain string token wherever you consume it since they are two different tokens entirely.@mgechev please consider scoping this specific
Request
injection functionality for a v17 release sometime prior to v18 and the May release. There’s obviously a lot of scope covered in these comments and this issue overall, but the above patch demonstrates that ensuringRequest
gets injected in the dev server is a self-contained, low-lift piece of work.Thanks for fielding this ticket and letting us know things are in the works!
if you enable prerender option in @angular-devkit/build-angular:application the build procces failed because i have RESPONSE and REQUEST injections in server.ts file and angluar can not run the app for saving prerender routes, the only way that app runs successfully is to run /dist/server/server.mjs with node and injection works.
https://github.com/angular/angular-cli/issues/26323#issuecomment-1954498130 use it without prerender or exclude 404 page from prerender.
@hittten perhaps you are missing the file that the migrations create for you:
https://stackoverflow.com/a/77459798
I had the same problem but looking around I found it. I haven’t seen that in the docs though and in my view it should be created by default when you select SSR enabled
@BeCaDRI, DI and provider tokens values will not be sent from the server to the client. DI functions identically on both the server and client applications, so there’s no specific documentation for server-side DI.
For data transfer from the server to the browser application, you should utilize the TransferState API.
@kamilpiusvs, in that case you are likely setting up DI incorrectly. Please open a new issue if the problem persists.
We are also facing issues when trying to perform 301s that we used to prior the migration to Angular 17.1.3. In reality we are having two issues:
When running
ng server
we are seeing that the response object comes back as null - as I read it looks like theserver.ts
is not used however I don’t know of any workaround to simulate the behaviour that we see when we deploy to PROD.When running on PROD, we are seeing that the actual Angular application hangs when hitting that response object which wasn’t happening at all when running Angular 16.
Our configuration for the server.ts is quite straightforward:
` // All regular routes use the Angular engine server.get(‘*’, (req, res, next) => { const { protocol, originalUrl, baseUrl, headers } = req;
});`
Yeah, devmode without using server.ts is also a nogo for our team.
We use the express framework for different things. For example to interact with cookies, redirects and integrate a SSO. I hope you can find a solution for that.
Thanks for your effords and the great framework.
Not a fix for the issue but if you want to use esbuild found there is a browser-esbuild builder you can drop into an existing angular.json build. If, like myself, you do most development without SSR and then just test in SSR this is an alright method of getting the esbuild benefits in development, but it does warn that its temporary so hopefully before its removed we will be able to use application builder.
This is expected as for
ng serve
theserver.ts
is not used.Whilst we can provide an optional request token, this will be not be same interface of the Express tokens as the dev-server does not use express. Also the mentioned tokens will not available when using prerendering.
The recommended approach to treat the tokens set in
server.ts
as optional.