NestJS 作为使用 SSR 的 Angular 19 的 BFF

问题描述 投票:0回答:1

Angular 19 对 SSR 的工作方式进行了重大改进。特别是,服务器部分现在在启动 vite dev 服务器时实际使用。这意味着我可以使用express作为我的反向代理,这样我就不必将我的代理定义为用于开发的json配置,并再次在代码中用于生产。太棒了!

这让我思考;我知道关注点分离,前端应该只做前端,后端应该只做后端,对吗?但是使用 SSR,我的前端已经由后端提供服务。为什么我应该为前端设置一个后端,为后端设置另一个单独的后端?嗯,我认为我不需要。对 @angular/ssr 包所做的更改使我能够拥有一个完整的expressjs 服务器作为我的 BFF 开发和产品服务器,并具有以下所有功能。因此,我不仅可以通过混合补水为我的前端提供服务并提供反向代理,还可以在这里定义我的实际后端 api。一切都来自于一个简单的

ng serve

但是在express中定义后端端点及其逻辑有点烦人。我想利用像 NestJS 这样的后端框架。这就是我停下来的地方,因为我无法让我的 NestJS 控制器在此设置中做出响应。

这有效:

function widgetRoutes(server: express.Express) {
  const widgetData = [
    { id: 1, name: 'Weather', componentName: 'weather' },
    { id: 2, name: 'Taxes', componentName: 'widget2' },
    { id: 3, name: 'Something else', componentName: 'widget3' },
  ];

  server.get('/api/widgets', (req, res) => {
    res.json(widgetData);
  });

  /**
   * Fetch a single widget by ID.
   */
  server.get('/api/widgets/:id', (req, res) => {
    if (req.params.id) {
      const widget = widgetData.find((w) => w.id === +req.params.id);
      if (!widget) {
        res.status(404).json({ error: 'Widget not found' });
      } else {
        res.json([widget]);
      }
    }
    console.log('[APP]', req.method, req.url, res.statusCode);
  });
}

export function bootstrap(): express.Express {
  const server = express();
  const serverDistFolder = dirname(fileURLToPath(import.meta.url));
  const browserDistFolder = resolve(serverDistFolder, '../browser');

  // Here, we now use the `AngularNodeAppEngine` instead of the `CommonEngine`
  const angularNodeAppEngine = new AngularNodeAppEngine();

  // Setup api routes
  widgetRoutes(server);

  // Setup reverse proxy routes
  Object.entries(proxyRoutes).forEach(([path, config]) =>
    server.get(path, createProxyMiddleware(config)),
  );

  // Serve static files from the browser distribution folder
  server.get(
    '**',
    express.static(browserDistFolder, {
      maxAge: '1y',
      index: 'index.html',
    }),
  );

  server.get('**', (req, res, next) => {
    // Yes, this is executed in devMode via the Vite DevServer
    console.log('[APP]', req.method, req.url, res.statusCode);

    angularNodeAppEngine
      .handle(req, { server: 'express' })
      .then((response) =>
        response ? writeResponseToNodeResponse(response, res) : next(),
      )
      .catch(next);
  });

  return server;
}

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

console.warn('Node Express server started');

// This exposes the RequestHandler
export const reqHandler = createNodeRequestHandler(server);

这不是:

@Controller('api/widgets')
export class WidgetController {
  widgetData = [
    { id: 1, name: 'Weather', componentName: 'weather' },
    { id: 2, name: 'Taxes', componentName: 'widget2' },
    { id: 3, name: 'Something else', componentName: 'widget3' },
  ];

  @Get()
  findAll() {
    return this.widgetData;
  }

  @Get(':id')
  findOne(@Param('id') id: string) {
    return this.widgetData.find((w) => w.id === +id);
  }
}

@Module({
  // Setup api routes
  controllers: [WidgetController],
})
export class AppModule {}
  
export async function bootstrap() {
  const app = await NestFactory.create<NestExpressApplication>(AppModule);
  // Get the express instance from the NestJS app
  const server = app.getHttpAdapter().getInstance();
  const serverDistFolder = dirname(fileURLToPath(import.meta.url));
  const browserDistFolder = resolve(serverDistFolder, '../browser');
  
  // Here, we now use the `AngularNodeAppEngine` instead of the `CommonEngine`
  const angularNodeAppEngine = new AngularNodeAppEngine();
  
  // Setup reverse proxy routes
  Object.entries(proxyRoutes).forEach(([path, config]) =>
    server.get(path, createProxyMiddleware(config)),
  );
  
  // Serve static files from the browser distribution folder
  server.get(
    '**',
    express.static(browserDistFolder, {
      maxAge: '1y',
      index: 'index.html',
    }),
  );

  server.get('**', (req, res, next) => {
    // Yes, this is executed in devMode via the Vite DevServer
    console.log('[APP]', req.method, req.url, res.statusCode);

    angularNodeAppEngine
      .handle(req, { server: 'express' })
      .then((response) =>
        response ? writeResponseToNodeResponse(response, res) : next(),
      )
      .catch(next);
  });

  return app;
}
  
const server = await bootstrap();
if (isMainModule(import.meta.url)) {
  const port = process.env['PORT'] || 4000;
  server.listen(port, () => {
    console.log(`Node Express server listening on http://localhost:\${port}`);
  });
}
  
// This exposes the RequestHandler
export const reqHandler = createNodeRequestHandler(
  server.getHttpAdapter().getInstance(),
);
 

我做错了什么?设置看起来是相同的,但 NestJS 控制器没有响应。 或者,如果我的想法很糟糕,为什么我不应该冒险走这条路呢?

angular nestjs server-side-rendering
1个回答
0
投票

开始工作了:

@Controller('api/widgets')
export class WidgetController {
  widgetData = [
    { id: 1, name: 'Weather', componentName: 'weather' },
    { id: 2, name: 'Taxes', componentName: 'widget2' },
    { id: 3, name: 'Something else', componentName: 'widget3' },
  ];

  @Get()
  findAll() {
    return this.widgetData;
  }

  @Get(':id')
  findOne(@Param('id') id: string) {
    return this.widgetData.find((w) => w.id === +id);
  }
}

@Module({
  // Setup api routes
  controllers: [WidgetController],
})
export class ApiModule {}
  
export async function bootstrap() {
  // Create the NestJS application
  const app = await NestFactory.create<NestExpressApplication>(ApiModule);
  // Get the Express instance
  const server = app.getHttpAdapter().getInstance();

  // Setup reverse proxy routes
  Object.entries(proxyRoutes).forEach(([path, config]) =>
    server.get(path, createProxyMiddleware(config)),
  );

  // Serve static files from the browser distribution folder
  const serverDistFolder = dirname(fileURLToPath(import.meta.url));
  const browserDistFolder = resolve(serverDistFolder, '../browser');
  server.get(
    '**',
    express.static(browserDistFolder, {
      maxAge: '1y',
      index: 'index.html',
    }),
  );

  // SSR middleware: Render out the angular application server-side
  const angularNodeAppEngine = new AngularNodeAppEngine();
  server.get('**', (req, res, next) => {
    angularNodeAppEngine
      .handle(req, { server: 'express' })
      .then((response) => {
        // If the Angular app returned a response, write it to the Express response
        if (response) {
          const n = writeResponseToNodeResponse(response, res);
          console.log('[SSR]', req.method, req.url, response.status);
          return n;
        }
        // If not, this is not an Angular route, so continue to the next middleware
        return next();
      })
      .catch(next);
  });

  // Initialize the NestJS application and return the server
  app.init(); // <-- This is what makes it work
  return server;
}
  
const server = await bootstrap();
if (isMainModule(import.meta.url)) {
  const port = process.env['PORT'] || 4000;
  server.listen(port, () => {
    console.log(`Node Express server listening on http://localhost:\${port}`);
  });
}
  
// This exposes the RequestHandler
export const reqHandler = createNodeRequestHandler(server);
 

我显然没有初始化 Nestjs 引擎。

app.init()
成功了。同样重要的是,这是在所有快速配置之后进行的。如果我在创建 Nestjs 服务器后将其放在右侧,则所有反向代理和 ssr 内容都会被忽略。

© www.soinside.com 2019 - 2024. All rights reserved.