使用 Nestjs 提供公共和私有端口服务

2023-12-05

我正在构建一个旨在为移动应用程序提供服务的应用程序。除了为客户提供服务外,它还将具有多种后台功能。

我们正在使用swagger我们确实希望能够访问后台端点的 swagger 文档。但是,我们不想公开暴露我们的所有端点。

假设将所有端点公开是一个糟糕的选择,我们正在考虑的一种解决方案是让我们的服务器服务两个端口,然后只向公众公开一个端口。我们创建了一个小样本库它在两个不同的端口上为客户端模块和后台模块提供服务。

The main.ts看起来像下面这样:

import { NestFactory } from '@nestjs/core';
import { ClientModule } from './modules/client/client.module';
import * as express from 'express';
import * as http from 'http';
import {ExpressAdapter} from '@nestjs/platform-express';
import { BackOfficeModule } from './modules/backoffice/backoffice.module';
import { SwaggerModule, DocumentBuilder } from '@nestjs/swagger';

async function bootstrap() {

  const clientServer = express();
  const clientApp = await NestFactory.create(
    ClientModule,
    new ExpressAdapter(clientServer),
  );
  const clientOptions = new DocumentBuilder()
    .setTitle('ClientServer')
    .setDescription('The client server API description')
    .setVersion('1.0')
    .addTag('client')
    .build();
  const clientDocument = SwaggerModule.createDocument(clientApp, clientOptions);
  SwaggerModule.setup('api', clientApp, clientDocument);
  await clientApp.init();

  const backOfficeServer = express();
  const backOfficeApp = await NestFactory.create(
    BackOfficeModule,
    new ExpressAdapter(backOfficeServer),
  );

  const backOfficeOptions = new DocumentBuilder()
    .setTitle('BackOffice')
    .setDescription('The back office API description')
    .setVersion('1.0')
    .addTag('backOffice')
    .build();
  const backOfficeDocument = SwaggerModule.createDocument(backOfficeApp, backOfficeOptions);
  SwaggerModule.setup('api', backOfficeApp, backOfficeDocument);
  await backOfficeApp.init();

  http.createServer(clientServer).listen(3000); // The public port (Load balancer will route traffic to this port)
  http.createServer(backOfficeServer).listen(4000); // The private port (Will be accessed through a bastian host or similar)
}
bootstrap();

另一种选择是对代码库和基础设施进行更大的分离,但由于这是一个非常早期的阶段,我们认为这是不必要的。

我们的问题是Nest社区就是这样,有人这样做过吗?如果是这样,您的经验是什么?像这样分离后端代码有什么缺点?


免责声明:该解决方案适用于express+REST组合。

Routing

即使nestjs不能根据端口分离控制器,它也可以根据主机分离它们。使用它,您可以在应用程序前面添加一个反向代理,该代理根据端口修改主机标头。或者,您可以在快速中间件中执行此操作,以使事情变得更加简单。这就是我所做的:

async function bootstrap() {
  const publicPort = 3000
  const privatePort = 4000

  const server = express()
  server.use((req, res, next) => {
    // act as a proper reverse proxy and set X-Forwarded-Host header if it hasn't been set
    req.headers['x-forwarded-host'] ??= req.headers.host
    switch (req.socket.localPort) {
      case publicPort:
        req.headers.host = 'public'
        break
      case privatePort:
        req.headers.host = 'private'
        break
      default:
        // this shouldn't be possible
        res.sendStatus(500)
        return
    }

    next()
  })

  const app = await NestFactory.create(AppModule, new ExpressAdapter(server))

  http.createServer(server).listen(publicPort)
  http.createServer(server).listen(privatePort)
}

控制器:

@Controller({ path: 'cats', host: 'public' })
export class CatsController {...}


@Controller({ path: 'internal' host: 'private' })
export class InternalController {...}

或者,您可以通过创建自己的 PublicController 和 PrivateController 装饰器来简化:

// decorator for public controllers, also sets guard
export const PublicController = (path?: string): ClassDecorator => {
  return applyDecorators(Controller({ path, host: 'public' }), UseGuards(JwtAuthGuard))
}

// decorator for private controllers
export const PrivateController = (path?: string): ClassDecorator => {
  return applyDecorators(Controller({ path, host: 'private' }))
}


@PublicController('cats')
export class CatsController {...}


@PrivateController('internal')
export class InternalController {...}

Swagger

对于 swagger,SwaggerModule.createDocument 有一个选项“include”,它接受要包含在 swagger 文档中的模块列表。通过一些努力,我们还可以将 swagger 服务部分变成一个快速路由器,这样私有和公共 swagger 都可以在同一路径上为不同的端口提供服务:

async function bootstrap() {
  const publicPort = 3000
  const privatePort = 4000

  const server = express()
  server.use((req, res, next) => {
    // act as a proper reverse proxy and set X-Forwarded-Host header if it hasn't been set
    req.headers['x-forwarded-host'] ??= req.headers.host
    switch (req.socket.localPort) {
      case publicPort:
        req.headers.host = 'public'
        break
      case privatePort:
        req.headers.host = 'private'
        break
      default:
        // this shouldn't be possible
        res.sendStatus(500)
        return
    }

    next()
  })

  const app = await NestFactory.create(AppModule, new ExpressAdapter(server))

  // setup swagger
  let publicSwaggerRouter = await createSwaggerRouter(app, [CatsModule])
  let privateSwaggerRouter: await createSwaggerRouter(app, [InternalModule])
  server.use('/api', (req: Request, res: Response, next: NextFunction) => {
    switch (req.headers.host) {
      case 'public':
        publicSwaggerRouter(req, res, next)
        return
      case 'private':
        privateSwaggerRouter(req, res, next)
        return
      default:
        // this shouldn't be possible
        res.sendStatus(500)
        return
    }
  })

  http.createServer(server).listen(publicPort)
  http.createServer(server).listen(privatePort)
}

async function createSwaggerRouter(app: INestApplication, modules: Function[]): Promise<Router> {
  const swaggerConfig = new DocumentBuilder().setTitle('MyApp').setVersion('1.0').build()

  const document = SwaggerModule.createDocument(app, swaggerConfig, { include: modules })

  const swaggerUi = loadPackage('swagger-ui-express', 'SwaggerModule', () => require('swagger-ui-express'))

  const swaggerHtml = swaggerUi.generateHTML(document)
  const router = Router()
    .use(swaggerUi.serveFiles(document))
    .get('/', (req: Request, res: Response, next: NextFunction) => {
      res.send(swaggerHtml)
    })

  return router
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

使用 Nestjs 提供公共和私有端口服务 的相关文章

  • 在从命令行运行的 NestJS 脚本中使用服务

    我知道如何从命令行运行脚本 使用npm or npx ts node script ts 正如所述here https stackoverflow com questions 60704316 run nestjs script from
  • 如何从控制器返回 PDF 文件

    我正在尝试使用 NestJs 从控制器端点返回 PDF 文件 未设置时Content typeheader 返回的数据getDocumentFile 很好地返回给用户 然而 当我添加标头时 我得到的返回似乎是某种奇怪形式的 GUID 响应总
  • Nodejs - 处理和发送多部分请求,

    我的应用程序在 Nodejs 服务器上运行 Node Js 还充当来自应用程序的请求的中间件 代理 因此 从浏览器开始 所有 REST 调用都会转到 NodeJs 然后转到 Java API 我发现处理多部分表单数据的请求时出现问题 我在
  • ExpressJS - DELETE 请求后 res.redirect

    我一直在寻找如何执行此操作 我正在尝试在发出删除请求后重定向 这是我正在使用的代码没有重定向 exports remove function req res var postId req params id Post remove id p
  • "message": "ENOENT: 没有这样的文件或目录,打开 'E:\\astrology\\utils\\uploads\\1600798534862qf.png'"

    正如标题所示 我得到error message ENOENT no such file or directory open E astrology utils uploads 1600798534862qf png 在我的项目中 即使在通过
  • Nestjs如何获取请求中的cookie?

    Nestjs如何获取请求中的cookie import Get Controller Response Request from nestjs common import AppService from app service const
  • 我应该在 Promise.all 中使用 wait 吗?

    我正在构建快速中间件 以对数据库进行两次异步调用 以检查用户名或电子邮件是否已在使用中 这些函数返回的承诺没有捕获 因为我想将数据库逻辑与 req res next 逻辑分开 并且我有集中的错误处理 需要next作为一个论点 在我对本地环境
  • 如何指定 Express 响应返回的类型

    我正在尝试使用 TypeScript 标准化我的express js Web应用程序中的响应 但我不太确定如何全局设置响应应该是这个接口 success boolean data any error string 现在我只是在写 async
  • Node / Express Handlebars - 在哪里定义自定义助手

    我正在开发一个节点 快速 车把应用程序 我刚刚发现了自定义助手 但我不知道在哪里定义它们 我尝试在实际视图模板 hbs 文件中添加一些
  • UnhandledPromiseRejectionWarning: MongoError: w 必须是连接处的数字或字符串

    任何人都知道为什么我会收到此错误 UnhandledPromiseRejectionWarning MongoError w 必须是连接处的数字或字符串 我在运行下面的代码时遇到此错误 它的目的是检查用户是否在 mongodb 数据库中 如
  • 在 Express Nodejs 中将图像文件转换为 Base64

    我正在尝试将图像文件转换为base64 这样我就可以以base64字符串形式存储在mongoDB中 这就是我尝试这样做的方式 router post file upload function req res function base64
  • 无法调用 Node.js 中 ES6 定义的类中的方法 [重复]

    这个问题在这里已经有答案了 我正在使用 Node js Express js 和 MongoDB 制作一个应用程序 我正在使用 MVC 模式 并且还有单独的路由文件 我正在尝试创建一个控制器类 其中一个方法调用其中声明的另一个方法 但我似乎
  • 使用 jasmine 测试 Express.js

    我正在学习 Node js 和 Express 框架 我是茉莉花的忠实粉丝 所以我想尽可能使用 jasmine 但是 我找不到用 jasmine 测试 Express 的好方法 例如 我应该如何在 app js 中测试路由 如果我在 app
  • Ember.js、Express.js 和 Node.js 的资产管道?

    我正在使用 Express js 作为后端构建 Ember js 应用程序 现在 我单独加载所有 js 文件并将 Handlebars 模板存储在 HTML 文件中 我喜欢用类似于 Rails 中的成熟的 资产管道 来替换 在完美的世界中
  • Node.js Express 网站实时更新数据

    我正在尝试实现一些我认为应该非常简单的事情 但我发现的所有教程和示例似乎都有点矫枉过正 我在做什么 我定期获取天气信息 并且我想在每次获取时更新网站上的文本 而无需用户刷新浏览器 几乎每个关于实时数据更新的教程都建议使用socket io
  • 如何将express.js服务器部署到Netlify

    我正在尝试将 Vue js Node Express MongoDB MEVN 堆栈应用程序部署到 Netlify 我成功地将应用程序的前端部署到 Netlify 现在正在尝试基于以下内容部署 Express 服务器serverless h
  • 错误:未指定默认引擎且未提供扩展名

    我正在使用 node js 和引擎设置 http 服务器 但是 我不断遇到一些问题 我对如何解决这个问题知之甚少 我希望能得到一些帮助来解决这个问题 Error No default engine was specified and no
  • Firebase Cloud Function:multipart/form-data 出现“意外的表单结束”错误

    我正在尝试在我的 Cloud Function Express 应用程序上创建文件上传路由 我尝试过不同的模块 例如formidable multer and busboy 但是在使用 firebase 模拟器运行服务器时 没有一个可以逃脱
  • 在next.js中获取客户端当前的url

    因此 我正在开发一个 Nodejs 应用程序 我将在该应用程序上建立我的新网站 并且我想为客户端的用户提供一种显示不同内容的方法 根据用户按下的内容重新呈现 我的想法是 例如 首先用户会看到 请先选择一个工具 然后用户将在导航栏中选择一个工
  • Gitlab CI/CD 管道给出 Dockerfile 错误

    晚上好 我正在尝试将我的nodeJS应用程序部署到我的Digital Ocean Server 它说找不到我的Dockerfile 我确实检查过 Dockerfile 没有 txt 扩展名 任何指导表示赞赏 我在 Gitlab 项目中设置了

随机推荐

  • MATLAB 中的矩阵运算

    我正在尝试简化我的代码 但遇到了一个小问题 让 v 1 2 3 a1 4 5 6 a2 7 8 9 A a1 a2 我的目标是计算 u v a1 v a2 仅使用v一度 这可能吗 是的 你可以使用bsxfun 例如 u bsxfun tim
  • `use std::io::Result as IOResult;` 比 `use std::io;` 仅仅引用 Rust 中的 `io::Result` 更高效吗?

    在学习 Rust 的过程中 我看到了人们导入库的多种方式 例如std io Result 例如 use std io Result as IOResult and pub type IOResult
  • R 中的表情符号 [UTF-8 编码]

    我正在尝试对 R 进行表情符号分析 我存储了一些带有表情符号的推文 这是我要分析的推文之一 gt tweetn2 1 Programme du week end xed xa0 xbd xed xb2 x83 xed xa0 xbc xed
  • Rails 迁移抱怨使用回形针的未定义方法“附件”

    Lemmie 在开头说我对 Rails 还很陌生 我们的应用程序使用回形针 3 2 4 来管理附件 像往常一样 我生成了一个类似于以下内容的迁移 class AddAttachmentPhotoToPhpfoxUsers lt Active
  • 是否有必要在 C# 中显式删除事件处理程序

    我有一堂课提供一些活动 该类是全局声明的 但不是在该全局声明上实例化的 它是根据需要在需要它的方法中实例化的 每次方法中需要该类时 都会对其进行实例化并注册事件处理程序 在方法超出范围之前是否有必要显式删除事件处理程序 当方法超出范围时 类
  • 为 Windows 上的 Lua 构建 lua 文件系统

    我对构建 makefile 没有任何线索 我正在尝试在 Windows 7 x86 中构建 luafilesystem 以便与 Lua for Windows 一起使用 我已经在互联网上搜索了教程 但我就是无法弄清楚 我在开发人员命令提示符
  • 如何使用 UIImagePickerController 显示所有相机控件?

    使用 UIImagePickerController 时 我无法显示相机控件 具体来说 我需要能够在慢动作 视频 照片 方形和全景之间进行选择 我使用的代码的基本部分是 UIImagePickerController pc UIImageP
  • 从 C# 修改任何窗口的不透明度

    是否可以从 C 修改所有打开的窗口的不透明度 我在 google 上搜索了最小化窗口 我发现这可以通过 pInvoke 调用实现 它甚至起作用了 同样 是否可以从 C 更改所有打开的窗口的不透明度 另外 我不喜欢 MFC 的东西 还有什么工
  • 测量执行单个指令的时间

    有没有办法使用 C 或汇编程序甚至 C 来准确测量执行 ADD 指令所需的时间 是的 有点 但它并不平凡 并且产生的结果是almost毫无意义 至少在最现代的处理器上是这样 在相对较慢的处理器上 例如 从英特尔系列中的原始奔腾处理器开始 在
  • 将 tf.keras.utils.image_dataset_from_directory 与标签列表一起使用

    我有目录示例中相应数量的文件的标签列表 1 2 3 train ds tf keras utils image dataset from directory train path label mode int labels train la
  • raw 文件夹中文本文件中的除号

    我的 android 的 txt 文件中有 当这个除法符号显示在 Android 设备上时 我看到 更糟糕的是 我的笔记本电脑键盘上没有除号 我使用了维基百科上的标志 猜猜我该如何解决这个问题 您应该对任何符号 例如此处的除号 使用 Uni
  • 如何使用jquery设置多个CSS显示属性值

    好吧 这让我有点抓狂 我正在使用 jQuery css 方法try设置类所需的多个 Flexbox 显示属性 问题是 它只保留最后一个 关于如何使用 jQuery 执行此操作的任何想法 或者这是不可能的 到目前为止 这是我尝试过的 depa
  • 如何指定CSS类的顺序?

    我对 CSS 和class属性 我一直认为 我在属性值中指定多个类的顺序是有意义的 后面的类可以 应该覆盖前面的定义 但这似乎不起作用 这是一个例子
  • 如何分配给匹配分支内的匹配表达式中使用的变量?

    我正在尝试实现一个通用功能join 它可以在任何迭代器的迭代器上工作 我的借用检查器有问题match里面的表达式next 方法实施 这是我的代码的简化版本 pub struct Join i where I Iterator I Item
  • C# - Excel 2013 如何更改图表样式

    我正在尝试更改 excel 2013 中图表的样式 但它只更改颜色 Range chartRange ChartObjects wsCharts ChartObjects workSheet ChartObjects Type Missin
  • 以编程方式将 id 添加到 R.id

    我正在创建一个EditText然后我尝试在单元测试中引用该对象 添加新内容的最佳方式是什么id to R id对于这个动态创建的对象 以便我稍后可以通过findViewById 在单元测试中 您可以使用 xml 资源文件设置稍后将在 R i
  • Bootstrap 4方格

    我想用 Bootstrap 4 创建一个响应式的正方形网格 为此 我正在做这样的事情 一行 div class container div class row div class col div div class col div div
  • 文本区域不能有默认值[重复]

    这个问题在这里已经有答案了 请检查这些行
  • Spring RedirectAttributes:addAttribute() 与 addFlashAttribute()

    到目前为止我的理解是我们可以指定您的控制器请求映射方法重定向属性参数并用请求重定向时的属性填充它 Example RequestMapping value hello method GET public String hello Redir
  • 使用 Nestjs 提供公共和私有端口服务

    我正在构建一个旨在为移动应用程序提供服务的应用程序 除了为客户提供服务外 它还将具有多种后台功能 我们正在使用swagger我们确实希望能够访问后台端点的 swagger 文档 但是 我们不想公开暴露我们的所有端点 假设将所有端点公开是一个