NestJS小技巧31-在NestJS中运行一次性操作

csharp 复制代码
by 雪隐 from https://juejin.cn/user/1433418895994094
本文欢迎分享与聚合,全文转载就不必了,尊重版权,圈子就这么大,若急用可联系授权

原文链接

最近,在工作中我不得不根据一个外部服务产生的外部ID来更新一些生产数据库的列。使用一次性操作进行数据操作的两个较大的优点(在我看来)是:1)你不需要手动访问生产数据库,2)你不需要将模式迁移与数据迁移混合。

您可以在以下仓库中找到关于这篇文章主题的更完整示例。

在商业中,我们有时需要在我们的后端或数据库中运行特定操作,且只运行一次。有些人称这些操作为一次性操作,因为它们应该只运行并完成一次。运行一次性操作具有巨大的威力,因为它允许使用生产数据执行某些操作(例如向特定用户群发送电子邮件)或根据外部服务更新生产数据。最近,我在工作中需要根据外部服务产生的外部ID更新一些生产数据库列。使用一次性操作处理数据的两个较大优势(在我看来)是:1)您不需要手动访问生产数据库,以及2)您不需要将模式迁移与数据迁移混合在一起。

目前,我正在使用NestJS工作,但我找不到如何使用它运行一次性操作的任何文档。我们决定创建工具来运行我们的一次性操作,这篇文章将描述实现这个想法所需的步骤。为了文章的目的,让我们想象一个场景,我们想要使用某种电子邮件服务向我们的管理员用户发送电子邮件。

为一次性操作创建一个触发器

在此需要考虑的一点是,我们很可能需要在代码运行时在生产环境中运行这个一次性操作。这意味着我们需要在运行时某种方式触发这个一次性函数。我们考虑了两种方案:一个我们可以通过Curl调用的特定端点,或一个NestJS的新入口点。创建一个新的端点会将其暴露给公众,需要一些安全措施来防止未经授权的访问,所以我们决定采用后者。当你开始开发一个NestJS应用程序时,通常会为你创建一个入口点文件,叫做main.ts 。这个文件加载你的服务器运行所需的所有必要模型、控制器和服务依赖关系。我们不需要所有这些来运行这个一次性操作,所以我们将创建一个新的入口点,只带有必要的依赖关系。我们称之为one-off.ts

ts 复制代码
// one-off.ts  
  
import { NestFactory } from '@nestjs/core';  
import { OneOffModule } from './one-off.module';  
  
async function oneOff() {  
  const application = await NestFactory.createApplicationContext(OneOffModule);  
  await application.close();  
}  
  
oneOff().catch(console.error);

最小的依赖注入

上面的示例显示了一个尚未定义的 OneOffModule。这个 NestJS 模块将包含运行 one-off 所需的所有依赖项。在我们的情况下,我们需要访问数据库并使用将发送电子邮件的服务。作为参考,您的 main.ts 文件加载了一个不同的模块,通常称为 AppModule,它加载了您应用程序的所有依赖项。将两个模块分开的原因是,我们不希望我们的 one-offs 因加载真实生产应用程序所拥有的所有依赖而导致加载时间增加。

ts 复制代码
// one-off.module.ts  
  
import { TypeOrmModule } from '@nestjs/typeorm';  
import { Module } from '@nestjs/common';  
import { SendEmailToAdmins } from './one-offs';  
import { EmailModule } from './email/email.module';  
  
@Module({  
  imports: [  
    EmailModule,  
    TypeOrmModule.forRoot({  
      type: 'mysql',  
      host: process.env.DB_HOST,  
      port: process.env.DB_PORT,  
      username: process.env.DB_USER,  
      logging: false,  
      password: process.env.DB_PASSWORD,  
      database: process.env.DB_NAME,  
      synchronize: false,  
    }),  
  ],  
  providers: [SendEmailToAdmins],  
  exports: [SendEmailToAdmins],  
})  
export class OneOffModule {}

如您所见,我们只初始化了一个到数据库的连接(我使用的是TypeORM,但您可以使用自己的库),并仅提供了在这种情况下我们想要运行的唯一的 one-off,即 SendEmailToAdmins 服务。

运行命令

我们为这个工具设置的一个要求是我们可以随时运行它,并运行特定的一次性操作。我们为了实现这一点首先创建了one-off.ts入口点。现在,经过编译后,我们可以简单地运行 node dist/one-off.js 来加载和运行这个入口点,但是目前的代码写法并不允许我们运行特定的一次性操作。为了实现这一点,我们需要告诉程序要运行哪个服务。为此,我们结合了使用Node的process.argv数组来获取在终端中传递的参数和使用命名导出的强大功能来查找、加载和运行一个特定的服务。

ts 复制代码
// one-off.ts  
  
import { NestFactory } from '@nestjs/core';  
import { OneOffModule } from './one-off.module';  
import * as Executables from './one-offs';  
  
async function oneOff() {  
  const klass = process.argv[2];  
  const application = await NestFactory.createApplicationContext(OneOffModule);  
  const oneOff = application.get(Executables[klass]);  
  
  try {  
    await oneOff.run();  
  } catch (error) {  
    console.error(error);  
  }  
  
  await application.close();  
}  
  
oneOff().catch(console.error);

如你所见,传递给终端的值随后用于 Executables 对象,该对象持有由一次性模块导出的所有服务。这增加了你被允许运行的安全性。此外,因为我们希望未来的开发者更容易地创建更多的一次性操作,所以我们为 OneOff 类定义了一个接口,其中 run 是唯一的公共方法。

如果你正在使用像 yarn 这样的工具,你只需在 package.json 的脚本中添加这样的内容: "one-off": "node dist/one-off.js",然后你可以用 yarn run one-off SendEmailToAdmins 来调用它。对于像 Heroku 这样的 PaaS,你可以使用 Heroku CLI 的 run 命令从本地机器运行这些命令。到目前为止,我们已经很成功,不再需要手动运行数据迁移,或者在生产中使用模式迁移工具。

未来的工作

到目前为止的工具非常有用,尽管我一直在考虑一个小的改进 。现在,没有任何东西禁止我通过使用Heroku的run命令并多次使用相同的参数来多次运行相同的一次性任务。防止这种情况的一种方法是向数据库添加一个新表,就像架构迁移工具那样,并跟踪已经运行的一次性任务。这将允许我们只运行一次性任务,并保留已经运行的一次性任务的某种历史记录。这对于审计目的非常有用。

总结

在这篇文章中,我们探讨了如何在NestJS中创建一个运行一次性任务的工具。我们看到了如何创建一个新的入口点,如何加载运行一次性任务所需的依赖项,以及如何使用终端运行特定的一次性任务。我们还看到了如何使用Heroku的CLI在生产中运行这些一次性任务。这种方法的明显优势是,我们不需要手动访问生产数据库,也不需要将架构迁移与数据迁移混合在一起。在行业中,开发者体验正越来越受到重视,我认为这是一个很好的例子,说明了如何改进它,不仅在于拥有完成繁重任务的工具,还在于记录开发努力和审计的方面。

本章代码

代码

相关推荐
橙子家13 分钟前
浏览器缓存之【结构化数据库与缓存】: IndexedDB、Cache storage 和 Storage buckets
前端
user205855615181318 分钟前
X6 中边悬浮置顶,规避 `mouseleave` 事件丢失问题
前端
李明卫杭州20 分钟前
CSS aspect-ratio 属性完全指南
前端
Pedantic2 小时前
SwiftUI 手势层级(Gesture Hierarchy)详解
前端
飘尘3 小时前
前端转型全栈(Java后端)的快速上手指引
前端·后端·全栈
一颗烂土豆3 小时前
Meshopt 压缩深度解析,为什么它比 Draco 更快
前端·javascript·webgl
浏览器工程师4 小时前
AI Agent 接浏览器任务,先别让它一路点到底
前端·后端
雨季mo浅忆4 小时前
VSCode自动格式化三要素
前端
爱勇宝5 小时前
深扒 Anthropic 1680 位工程师简历:应届生几乎没机会,AI 公司最缺的不是博士
前端·后端·程序员