Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

@angular/ssr/node not working as intended #3596

Open
davidbusuttil opened this issue Dec 20, 2024 · 2 comments
Open

@angular/ssr/node not working as intended #3596

davidbusuttil opened this issue Dec 20, 2024 · 2 comments

Comments

@davidbusuttil
Copy link

davidbusuttil commented Dec 20, 2024

I am using @angular/[email protected], and I have the following:

src/server.ts

import {
AngularNodeAppEngine,
createNodeRequestHandler,
isMainModule,
writeResponseToNodeResponse,
} from "@angular/ssr/node";

import express from "express";

import { dirname, resolve } from "node:path";
import { fileURLToPath } from "node:url";

const serverDistFolder = dirname(fileURLToPath(import.meta.url));
const browserDistFolder = resolve(serverDistFolder, "../browser");

const server = express();
const angularNodeAppEngine = new AngularNodeAppEngine();

server.use(
express.static(browserDistFolder, {
maxAge: "1y",
index: false,
redirect: false,
}),
);

server.use("/**", (serverRequest, serverResponse, serverNextFunction) => {
angularNodeAppEngine
.handle(serverRequest)
.then((response) => {
if (response) {
return writeResponseToNodeResponse(response, serverResponse);
} else {
return serverNextFunction();
}
})
.catch(serverNextFunction);
});

if (isMainModule(import.meta.url)) {
const port = process.env["PORT"] || 4000;
server.listen(port, () => {
console.log(Node Express server listening on http://localhost:${port});
});
}

export const reqHandler = createNodeRequestHandler(server);

src/app/app.routes.ts

import { Route } from "@angular/router";

import { ErrorComponent } from "./error/error.component";
import { errorResolver } from "./error/error.resolver";
import { HomeComponent } from "./home/home.component";
import { homeResolver } from "./home/home.resolver";

export const routes: Route[] = [
{
path: "error",
component: ErrorComponent,
resolve: {
error: errorResolver,
},
},
{
path: "",
pathMatch: "full",
component: HomeComponent,
resolve: {
home: homeResolver,
},
},
{
path: "**",
redirectTo: "/error",
},
];

src/app/app.routes.server.ts

import { RenderMode, ServerRoute } from "@angular/ssr";

export const serverRoutes: ServerRoute[] = [
{
path: "**",
renderMode: RenderMode.Server,
},
];

src/app/app.config.ts

import {
ApplicationConfig,
provideExperimentalZonelessChangeDetection,
} from "@angular/core";
import { initializeApp, provideFirebaseApp } from "@angular/fire/app";
import { getAnalytics, provideAnalytics } from "@angular/fire/analytics";
import { getFirestore, provideFirestore } from "@angular/fire/firestore";
import { getStorage, provideStorage } from "@angular/fire/storage";
import {
provideClientHydration,
withEventReplay,
} from "@angular/platform-browser";
import {
provideRouter,
withEnabledBlockingInitialNavigation,
withInMemoryScrolling,
} from "@angular/router";

import { routes } from "./app.routes";

import { environment } from "../environments/environment.production";

export const appConfig: ApplicationConfig = {
providers: [
provideExperimentalZonelessChangeDetection(),
provideClientHydration(withEventReplay()),
provideRouter(
routes,
withEnabledBlockingInitialNavigation(),
withInMemoryScrolling({
anchorScrolling: "enabled",
scrollPositionRestoration: "enabled",
}),
),
provideFirebaseApp(() => {
return initializeApp(environment.firebase);
}),
provideAnalytics(() => {
return getAnalytics();
}),
provideFirestore(() => {
return getFirestore();
}),
provideStorage(() => {
return getStorage();
}),
],
};

src/app/app.config.server.ts

import { mergeApplicationConfig, ApplicationConfig } from "@angular/core";
import { provideServerRendering } from "@angular/platform-server";
import { provideServerRoutesConfig } from "@angular/ssr";

import { appConfig } from "./app.config";
import { serverRoutes } from "./app.routes.server";

const serverConfig: ApplicationConfig = {
providers: [
provideServerRendering(),
provideServerRoutesConfig(serverRoutes),
],
};

export const config = mergeApplicationConfig(appConfig, serverConfig);

Now, I am not using ng deploy or firebase deploy since they require @angular-devkit/build-angular and the SSR function being deployed is not working. Instead, I created a separate Firebase Repo and initialised both hosting and functions.

firebase.json

{
"functions": [
{
"source": "functions/ssr",
"codebase": "ssr",
"ignore": [...]
}
],
"hosting": {
"public": "hosting",
"ignore": [...],
"rewrites": [
{
"source": "**",
"function": {
"functionId": "ssr",
"region": "europe-west1"
}
}
]
}
}

functions/ssr/index.js

import { onRequest } from "firebase-functions/v2/https";

import { reqHandler } from "./build/template/server/server.mjs";

export const ssr = onRequest(
{
region: "europe-west1",
invoker: "public",
memory: "256MiB",
cpu: 1,
timeoutSeconds: 60,
concurrency: 80,
minInstances: 1,
maxInstances: 100,
ingressSettings: "ALLOW_ALL",
preserveExternalChanges: false,
},
reqHandler,
);

In the Firebase Repo, I am successfully deploying both hosting and functions using firebase deploy. The SSR function works, save for paths redirected to the ErrorComponent:

src/app/app.routes.ts

{
path: "**",
redirectTo: "/error",
},

When seeing the Google Cloud Console logs for the Cloud function, the express instance is giving a 302 response (which is what you'd expect from a redirect), however, instead of staying on port 80, the redirect URL is given on port 8080, which is the internal port being using by the express instance. I reckon that server.ts needs to be modified, however, I don't know how.

@google-oss-bot
Copy link

This issue does not seem to follow the issue template. Make sure you provide all the required information.

@brunostuani
Copy link

I'm having the same issue

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants