Hacktoberfest 2026: los issues que los mantenedores marcaron para octubre, abiertos y aptos para principiantes. Explorar issues de Hacktoberfest

allow proxy config during ng build when using `outputMode: server`

Abierto
#29,188 5 comentarios 1 reacción 0 asignados Ver en GitHub

Nadie ha tomado este issue todavía.

Evaluación

Dificultad
4/5
Tiempo estimado
3-5 días
Aptitud para principiantes
42/100
Tipo de issue
Nueva funcionalidad
Claridad
Bastante claro
Estado de actividad
Estancado
Stack tecnológico
angular, typescript
Área
build-system, cli

Línea de trabajo

Comienza con el flujo reportado de ng build para outputMode: server y compáralo con el comportamiento de la configuración del proxy de ng serve. Revisa el controlador de solicitudes de server.ts y la ruta catch-all de serve-angular-ssr.ts proporcionados para entender dónde las solicitudes a /api entran en recursión. Se considera terminado cuando el build puede extraer rutas sin requerir un fallback de la aplicación para esas solicitudes de API.

Escrito por el modelo de indexación a partir del texto del issue.

Descripción

angular/build:application area: @angular/build area: @angular/ssr freq1: low severity3: broken type: bug/fix
Command

build

Description

I am facing a separate, but similar issue to #29005
@alan-agius4, in my case, the 30-sec timeout occurs because I am making api calls.

My setup is pretty standard:

  • 2 angular 'client' apps (ssr with fastify)
  • one 'server' api (fastify)

The '/api' route on both clients are expected to point to the same api instance

In production, '/api' on both client apps are routed the api via nginx and docker configuration
In development, I serve both the api and client locally, and use ng serve's proxy config

This means I can avoid having to stream my api data through my client's container. This is especially a win for production environment.

Because of this setup, I do not need to have a separate 'api' route in my client application.

BUT...

When angular tries to build my app, to extract the routes,

  • it makes request to the angular engine
  • in order to render the app, angular engine needs to make some api calls. Those api calls go to '/api'.
  • but there is no nginx config, or proxy config, so it passes the request to the client's fastify instance.
  • the client fastify instance does not have a handler for '/api' so it passes the request to the '*' route
  • '*' route is handled by.... angular engine

And so it loops and loops.

A. It was not immediately obvious that is a problem of recursion, and not a problem of route not able to render.
B. This is probably behaving as intended. But it forces developers to implement a fallback for api routes, or any other route that is expected on the same origin, just to build in peace. This was not needed before. We did not need to have runtime things available at build-time.
C. Yes, api route example was there in the initial generated server.ts. But it did not tell me "DO NOT DELETE THIS - YOU WILL NEED THIS LATER". Basically, maybe a more thorough documention is in order
D. Alternatively, would it not be great if we configure the build action to pick up proxy information, just like ng serve?


Following are the relevant files for the server setup
server.ts
import { createNodeRequestHandler, isMainModule } from '@angular/ssr/node';
import { serveAngularSSR } from '@easworks/app-shell/utilities/angular-ssr';
import { fastifyCors } from '@fastify/cors';
import { fastify, FastifyInstance } from 'fastify';
import * as path from 'node:path';
import { fileURLToPath } from 'node:url';
import { parseEnv } from 'server-side/environment';
import { useProblemDetailsGlobally } from 'server-side/utils/fastify-problem-details';
import { getLoggerOptions } from 'server-side/utils/logging';
import { printRoutes } from 'server-side/utils/print-routes.plugin';

const envId = parseEnv.nodeEnv();

async function initServer() {
  // const options = development ? {} : { http2: true };
  const options = {};

  const server = fastify({
    ...options,
    logger: getLoggerOptions(envId)
  });

  return server;
}

async function configureServer(server: FastifyInstance) {

  server.register(useProblemDetailsGlobally);
  server.register(printRoutes);

  server.register(serveAngularSSR, {
    directory: path.resolve(fileURLToPath(import.meta.url), '../..'),
  });

  await server.register(fastifyCors, {
    origin: true
  });
}

const server = await initServer();
await configureServer(server);

async function closeServer(server: FastifyInstance) {
  await server.close();
  process.exit();
}

/**
 * Start the server if this module is the main entry point.
 * The server listens on the port defined by the `PORT` environment variable, or defaults to 4000.
 */
if (isMainModule(import.meta.url)) {
  const host = '0.0.0.0';
  const port = Number.parseInt(process.env['PORT'] as string);

  try {
    await server.listen({ host, port });
    process.on('SIGTERM', () => closeServer(server));
    process.on('SIGINT', () => closeServer(server));
  }
  catch (e) {
    server.log.fatal(e);
    closeServer(server);
  }
}

/**
 * The request handler used by the Angular CLI (dev-server and during build).
 */
export const reqHandler = createNodeRequestHandler(async (req, res) => {
  await server.ready();
  server.server.emit('request', req, res);
});


serve-angular-ssr.ts
import { AngularNodeAppEngine, writeResponseToNodeResponse } from '@angular/ssr/node';
import { fastifyStatic } from '@fastify/static';
import { FastifyPluginAsync } from 'fastify';
import fastifyPlugin from 'fastify-plugin';
import mime from 'mime';
import * as path from 'path';

interface ServeAngularSSROptions {
  directory: string;
}

const pluginImpl: FastifyPluginAsync<
  ServeAngularSSROptions
> = async (server, options) => {
  const engine = new AngularNodeAppEngine();
  const browserDirectory = path.resolve(options.directory, 'browser');

  server.register(fastifyStatic, {
    root: browserDirectory,
    serve: false,
    index: false,
    redirect: false,
    maxAge: '1y',
  });

  const htmlMime = mime.getType('html');
  if (!htmlMime) throw new Error('invalid operation');

  server.get('*', async (req, reply) => {

    const extension = mime.getType(req.url);

    if (extension && req.url !== '/index.html') {
      return reply.sendFile(req.url);
    } else {
      reply.type(htmlMime);
      const response = await engine.handle(req.raw);
      if (response) return writeResponseToNodeResponse(response, reply.raw);
      else return reply.send();
    }

  });
};

export const serveAngularSSR = fastifyPlugin(pluginImpl, { name: 'serveAngularSSR' });
Describe the solution you'd like

No response

Describe alternatives you've considered

No response

Lenguaje dominante
TypeScript
Estrellas
27k
Forks
11.8k
Merge medio
17 h 25 min
PR fusionados (30 d)
183

Guía de contribución

Abrir la guía de contribución

Primeros pasos

  1. Lee el issue completo y luego la guía de contribución del proyecto.
  2. Comenta en el issue que vas a ocuparte — evita que dos personas hagan lo mismo.
  3. Haz un fork del repositorio y trabaja en una rama.
  4. Abre un pull request que haga referencia al número del issue.

Más de angular/angular-cli

Todos los issues de angular/angular-cli

Issues similares

Más issues de TypeScript

Recibe los nuevos issues en tu correo

Un resumen breve de issues de GitHub para principiantes.