Nestjs框架: nestjs-schedule模块注册流程,源码解析与定时备份数据库

nestjs 源码分析 ##NestJs 模块载入机制 ###AppModel初始化流程图 Created with Raphaël 2.2.0初始化应用调用NestFacotry.create()是否传入httpServer?使用用户传入的HttpServer初始化HttpServer创建ApplicationConfiguration创建NestContainer初始化AppModel模块实例扫描AppModel的引... 阅读详情

概述

  • 接触过Linux的小伙伴,应该知道Linux系统上有定时任务,而Windows系统上叫做计划任务
  • 不论哪种系统,大家或多或少都了解过定时任务,它可以通过系统脚本实现
  • 基于 NestJS定时任务,确实可以通过系统级脚本实现对MongoDB数据库的维护
  • 在管理的是日志部分的数据库,数据库需要进行滚动和备份操作, 就需要定时任务
  • NestJS的定时任务功能,有以下两个优点:
    • 不受平台限制
      • 它跟随业务系统运行,只要平台上有Node.js环境
      • 业务系统就能运行,定时任务也能生效
    • 支持多种任务类型
      • NestJS的定时任务既支持静态定时任务(即随着业务系统启动就注册好)
      • 也支持动态定时任务我们可以通过接口动态添加定时任务,这里有个定时任务示例叫addCronJob
        addCronJob(name: string, seconds: string) {
          const job = new CronJob(`${seconds} * * * * *`, () => {
            this.logger.warn(`time (${seconds}) for job ${name} to run!`);
          });
        
          this.schedulerRegistry.addCronJob(name, job);
          job.start();
        
          this.logger.warn(
            `job ${name} added for each minute at ${seconds} seconds!`,
          );
        }
        

场景

  • 定时任务有很多应用场景,比如计划类型的业务和通知类型的业务
  • 例如,我们每天要检查系统中的计划是否按照用户设定的节点推进,如果没有推进,就可以添加定时任务,每隔一段时间给用户发送通知
  • 当然,这里设置的定时任务通常是批量操作,而非为每个用户单独创建。添加定时任务后,其逻辑可能是根据用户数据库关联表格查询对应字段,然后给用户发送通知或消息

配置

下面来实操NestJS定时任务,并针对当前应用场景完成数据库部分数据的定时备份、滚动和清理

1 ) 配置NestJS服务端内容

  • 安装依赖:我们需要安装一个名为@nestjs/schedule的依赖

  • 配置定时任务相关内容

    • 我们需要设置一个环境变量 在 .env 类似相关文件中设置CRON_ON为true
      • CRON_ON=true
    • 在app.module.js模块或其他拆分管理的module模块中注册:
      import { Module } from '@nestjs/common';
      import { ScheduleModule } from '@nestjs/schedule';
      
      @Module({
        imports: [
          ScheduleModule.forRoot()
        ],
      })
      export class AppModule {}
      
    • 这样,是全局生效,我们也可以根据配置来根据配置进行条件生效,
    • 如, 新建 conditional.module.ts文件来做拆分管理
      import { Module } from '@nestjs/common';
      import * as dotenv from 'dotenv';
      import { toBoolean } from '../utils/format';
      import { MailModule } from './mail/mail.module';
      import { ScheduleModule } from '@nestjs/schedule';
      import { TasksService } from '@/common/cron/tasks.service';
      // import { StorageModule } from './storage/storage. module';
      
      const imports =[];
      const providers = [];
      
      const conditionalImports = () => {
      	const envFilePaths = [
      		`.env.${process.env.NODE_ENV |`development`}`,
      		'.env',
      	];
      	const parsedConfig = dotenv.config({ path:'.env'}).parsed;
      	envFilePaths.forEach((path) => {
      		if (path ==',env') return;
      		constconfig = dotenv.config({path});
      		Object.assign(parsedConfig,config.parsed);
      	});
      	if(toBoolean(parsedConfig['MAIL_ON'])){
      		imports.push(MailModule);
      	}
      	if(toBoolean(parsedConfig['CRON_ON']){
      		imports.push(ScheduleModule.forRoot());
      		providers.push(TasksService);
      	}
      	return imports;
      };
      
      @Module({
      	imports: conditionalImports(),
      	providers,
      });
      
      export class ConditionalModule {}
      
    • 将 conditional.module.ts 加入 app.module.js 中
  • 创建任务服务类:新建 common/cron目录,在下面新建一个tasks.service.ts文件,导出TasksService类

    import { Injectable } from '@nestjs/common';
    import { Cron } from '@nestjs/schedule';
    
    @Injectable()
    export class TasksService {
    	@Cron('******')
    	handleCron(){
    		console. log('test');
    	}
    }
    
    • 使用@Injectable()装饰器,可参考官方示例
    • 在类中调用console.log进行打印,其中cron是定时表达式,写六个星号代表每秒执行一次
  • 使用定时任务:在上面的 conditional.module,将TasksService添加到providers数组中,已处理

  • 这块,条件模块可以优化

    import { Module } from '@nestjs/common';
    import * as dotenv from 'dotenv';
    import { toBoolean } from '../utils/format';
    import { MailModule } from './mail/mail.module';
    import { ScheduleModule } from '@nestjs/schedule';
    import { TasksService } from '@/common/cron/tasks.service';
    // import { StorageModule } from './storage/storage. module';
    
    const imports =[];
    const providers = [];
    const exportsService = [];
    
    @Module({
    	imports: conditionalImports(),
    	providers,
    });
    
    export class ConditionalModule {
    	static register(): DynamicModule {
    
    		const envFilePaths = [
    			`.env.${process.env.NODE_ENV |`development`}`,
    			'.env',
    		];
    		const parsedConfig = dotenv.config({ path:'.env'}).parsed;
    		envFilePaths.forEach((path) => {
    			if (path ==',env') return;
    			constconfig = dotenv.config({path});
    			Object.assign(parsedConfig,config.parsed);
    		});
    		if(toBoolean(parsedConfig['MAIL_ON'])){
    			imports.push(MailModule);
    		}
    		if(toBoolean(parsedConfig['CRON_ON']){
    			imports.push(ScheduleModule.forRoot());
    			providers.push(TasksService);
    			exportsService.push(TasksService);
    		}
    
    		imports.push(conditionalImports());
    
    		return {
    			module: ConditionalModule,
    			imports,
    			providers,
    			exports: exportsService
    		}
    	}
    }
    
  • 在 app.module.ts 中使用 ConditionalModule 的时候

    • 替换为: ConditionalModule.register()

2 )源码解析

  • NestJS的定时任务注册主要在onApplicationBootstrap这个生命周期钩子方法中进行
  • 我们可以查看@nestjs/schedule的官方源码,在相关文件中 schedule/lib/scheduler.orchestrator.ts
  • 可以找到onApplicationBootstrap方法, 定时任务就在这里注册
  • 当应用关闭时,会清除对应的定时器、计时器以及定时任务
    onApplicationBootstrap(){
    	this.mountTimeouts();
    	this.mountIntervals();
    	this. mountCron();
    }
    
    onApplicationShutdown(){
    	this. clearTimeouts();
    	this.clearIntervals();
    	this.closeCronJobs();
    }
    

3 ) 定时任务备份数据库

  • 参考文档: backup-and-restore-tools/#deployments
  • 定时任务主要有两个
    • 连接到 MongoDB 并导出 connections 数据
    • 删除已有的 connections 数据
  • 删除数据时,需要删除两部分内容
    • 一是当前 connections 中的已备份数据,避免重复备份导致日志文件越来越大
    • 二是对比备份时间,删除超过一定天数或规则的先前备份的 connections 数据,实现滚动记录,防止磁盘爆满
  • 注意,这里不考虑集群分片的备份,一般集群会用分布式日志系统,如: ELK
  • 更多参考上述文档或请教专业运维,下面仅演示单机

MongoDB 备份与恢复

  • 要实现备份,需要了解如何在 Docker 中执行 mongodump 命令

  • 在 Docker 中有多个 MongoDB 实例,加入要导出端口为 27017 的 MongoDB 中 connections 名为 log 的数据

  • 执行命令如下:

    docker exec -it <容器名称> mongodump --uri=<数据库连接> --collection=log --out=/tmp/<时间戳>-log 
    
  • 如:

    docker exec -it nestjs-starter-mongo-1 mongodump --uri=mongodb://root:exmaple@localhost:27017/nest-logs --collection=log --out=/tmp/2025-06-06-log
    
  • 上述命令将数据备份到 Docker 容器内的临时目录 /tmp 下

  • 可以使用以下命令验证备份是否成功:

    docker exec -it <容器名称> ls -la /tmp/<时间戳>-log 
    
  • 如:

    docker exec -it nestjs-starter-mongo-1 ls -la /tmp
    
  • 若要恢复数据,MongoDB 提供了 mongorestore 命令。执行命令如下:

    docker exec -it <容器名称> mongorestore --uri=<数据库连接> --nsInclude=<源数据库名称> <指定的备份目录路径>
    
  • 如:

    docker exec -it nestjs-starter-mongo-1 mongorestore -uri=mongob:/root:exmaple@localhost:27017/nest-logs --nsInclude="nest-logs.log" /tmp/2025-06-06-log/nest-logs
    
  • 为避免覆盖正在使用的 connection,恢复时应指定新的 connection 名称(文件路径), 以便不影响正在收集的 connection

  • 例如,从 netlogs 中读取数据并恢复到 netlogs.log-2025-06-06 这个新的 connection 上,若该 connection 不存在则会自动创建

    docker exec -it nestjs-starter-mongo-1 mongorestore -uri=mongob:/root:exmaple@localhost:27017/nest-logs --nsFrom="nest-logs.log" --nsTo="nest-logs.log-2025-06-06" /tmp/2025-06-06-log/nest-logs
    
  • 也就是,如果需要查看历史备份,通常会将其恢复到一个全新名称的 connection 上,而不是覆盖原有的 connection

  • 现在,我们使用命令来做,每次都是手动的,还是比较麻烦的,我们想要使用定时任务来做,就需要完善定时任务的脚本

  • 这里推荐一个 ssh 的工具: ssh2

    • 这里基于文档,可以封装一个 ssh 相关的模块
    • 来提供 sshService 服务
    • 这里细节在最下面提供
  • 现在补充定时任务,编辑 common/cron/tasks.service.ts 示例如下:

    import { Injectable } from '@nestis/common';
    import { Cron } from '@nestjs/schedule';
    import { SshService } from '@/utils/ssh/ssh.service';
    
    @Injectable()
    export class TasksService {
    	constructor(private sshService: SshService){}
    	
    	@Cron('* * ** * *')
    	handleCron(){
    		//备份:连接到MongoDB并导出对应的db中的collections的数据
    		//滚动记录:删除已有的collections的数据
    		//1.删除当前collections中的已备份数据
    		//2.之前备份的collections->对比collection备份的时间,如果超过t天/hours的规则,则删除
    		const containerName = 'mongo-mongo-1';
    		const uri='mongodb: //root: example@localhost: 27017/nest-logs';
    		const now = new Date();
    		const collectionName = 'log';
    		const outputPath =`/tmp/logs-${now.getTime()}`;
    		const hostBackupPath = '/srv/logs';
    		const cmd =`docker exec -i ${containerName} mongodump --uri=${uri} --collection=${collectionName} --out=${outputPath}`;
    		const cpCmd =`docker cp ${containerName}:${outputPath} ${hostBackupPath}`;
    		await this.sshService.exec(`${cmd} && ${cpCmd}`).catch((err) => err) // 这里 catch 后续可以不用,因为没有 reject 了,参考下面
    		await this.sshService.exec(`ls-la ${hostBackupPath}`);
    		const delCmd = `find ${hostBackupPath} -type d -mtime +30 -exec rm
    		-rf {}\\;`;
    		await this.sshService.exec(delCmd).catch((err)=>err); // 这里 catch 后续可以不用,因为没有 reject 了,参考下面
    		const res = await this.sshService.exec(`ls-la ${hostBackupPath}`);
    		console.log('~ TasksService ~ handleCron ~ res:', res);
    	}
    }
    

封装 ssh 相关服务参考


1 )目录结构

  • ssh

    • ssh-core.module.ts
    • ssh.interface.ts
    • ssh.constants.ts
    • ssh.module.ts
    • ssh.service.ts
  • 生成相关组件

    • $ nest g mo utils/tools --no-spec -d --flat 生成 utils/tools.module.ts 不会有 utils 目录
    • $ nest g mo utils/ssh --no-spec -d 生成 utils/ssh/ssh.module.ts 这里有一层 ssh 目录
    • 这时候 两个模块就创建好了 在 tools.module 中会导入 ssh.module

2 ) 安装依赖

  • $ pnpm i ssh2
  • $ pnpm i @type/ssh2 -D

3 ) ssh.module.ts 模块

  • 创建2个 方法:forRoot (直传 options), forRootAsync ( 工厂方法, useFactory 依赖注入 inject configService)

  • 核心代码

    import { DynamicModule, Module } from '@nestjs/common'; 
    import { SshCoreModule } from './ssh-core.module';  
    import { SshModuleOptions, SshModuleAsyncOptions } from './ssh.interface';
    
    @Module({}) 
    export class SshModule { 
      // forRoot -> 直传 options 
      static forRoot(options: SshModuleOptions, name?: string): DynamicModule { 
        return { 
          module: SshModule, 
          imports: [SshCoreModule.forRoot(options, name)],
        }; 
      } 
     
      // forRootAsync -> useFactory -> inject configService 
      static forRootAsync(options: SshModuleAsyncOptions, name?:string): DynamicModule { 
        return { 
          module: SshModule, 
          imports: [SshCoreModule.forRootAsync(options, name)], 
        }; 
      } 
    } 
    
  • 这里外层仅作为不变的接口,核心逻辑 都在 ssh-core 和 ssh.service 中

4 ) ssh.interface.ts

核心代码

import { ModuleMetadata, Type } from '@nestjs/common'; 
import { ConnectConfig } from 'ssh2';
 
// 定义一个工厂接口,用于异步创建 SSH 模块选项 
export interface SshOptionsFactory { 
  // 定义一个抽象方法,用于创建 SSH 模块选项 
  createSshOptions(): Promise<SshModuleOptions> | SshModuleOptions; 
}

// 定义 SSH 模块选项接口,继承自 ssh2 的 ConnectConfig 
export interface SshModuleOptions extends ConnectConfig {} 
 
// 定义异步 SSH 模块选项接口,继承自 ModuleMetadata 的 imports 属性 
export interface SshModuleAyncOptions extends Pick<ModuleMetadata, 'imports'> {
  // 使用现有的工厂类
  useExisting?: Type<SshOptionsFactory>;
  // 使用新的工厂类
  useClass?: Type<SshOptionsFactory>;
  // 使用工厂函数
  useFactory?: (
    ...args: any[]
  ) => SshModuleOptions | Promise<SshModuleOptions>;
  // 注入依赖项
  inject?: any[];
}

5 )通用变量抽离 ssh.constants.ts

export const SSH_OPTIONS = Symbol('SSH_OPTIONS');

6 )ssh-core.module.ts

import { DynamicModule, Global, Module, Type, Provider } from '@nestjs/common'; 
import { SshModuleAsyncOptions, SshModuleOptions, SshOptionsFactory } from './ssh.interface';  
import { SSH_OPTIONS } from './ssh.constants';

@Module({}) 
@Global()
export class SshCoreModule { 
  // forRoot -> 直传options 
  static forRoot(options: SshModuleOptions, name?: string): DynamicModule {
    const sshOptionsProvider: Provider = { 
      provide: name ? name + `:${SSH_OPTIONS}` : SSH_OPTIONS,
      useValue: options, 
    };

    const sshClientProviders: Provider[] = [];
    if (name) {
    	sshClientProviders.push({
    		provide: `${name}:SSH`,
    		useFactory: async () => {
    			const ssh = new SshService(options);
    			await ssh.connect();
    			return ssh;
    		}
    	});
    } else {
    	sshClientProviders.push(SshService)
    }

    return {
		module: SshCoreModule,
        providers: [sshOptionsProvider, ...sshClientProviders],
        exports: [...sshClientProviders],
	}
  } 
 
  static createAsyncProviders(options: SshModuleAsyncOptions): Provider[] { 
    if (options.useExisting  || options.useFactory)  { 
      return [this.createAsyncOptionsProvider(options)]; 
    } 
    const useClass = options.useClass  as Type<SshOptionsFactory>; 
    return [
      this.createAsyncOptionsProvider(options),  
      { 
        provide: useClass, 
        useClass, 
      }, 
    ]; 
  } 
 
  static createAsyncOptionsProvider(options: SshModuleAsyncOptions): Provider { 
    if (options.useFactory)  { 
      return { 
        provide: SSH_OPTIONS,
        useFactory: options.useFactory,  
        inject: options.inject  || [], 
      }; 
    } 
    const inject = options.useClass  || (options.useExisting  as Type<SshOptionsFactory>); 
    return { 
      provide: SSH_OPTIONS,
      useFactory: async (optionsFactory: SshOptionsFactory) => await optionsFactory.createSshOptions(),  
      inject: [inject], 
    }; 
  } 
 
  // forRootAsync -> useFactory -> inject configService
  static forRootAsync(options: SshModuleAsyncOptions, name?: string): DynamicModule {
    const asyncProviders = this.createAsyncProviders(options);

    const sshClientProviders: Provider[] = [];

    if (name) {
    	sshClientProviders.push({
    		provide: `${name}:SSH`,
    		useFactory: async (options: SshModuleOptions) => {
    			const ssh = new SshService(options);
    			await ssh.connect();
    			return ssh;
    		},
    		inject: [SSH_OPTIONS],
    	});
    } else {
    	sshClientProviders.push(SshService)
    }
    
    return { 
      module: SshCoreModule, 
      providers: [...asyncProviders, ...sshClientProviders],
      exports: [...sshClientProviders],
    }; 
  } 
} 

7 )ssh.service.ts

import { Inject, Injectable } from '@nestjs/common'; 
import { SshModuleOptions } from './ssh.interface';  
import { SSH_OPTIONS } from './ssh.constants';
import { Client } from 'ssh2'; 
import * as fs from 'fs'; 

@Injectable() 
export class SshService {
  // inject 'SSH_OPTIONS' -> Client connect(options) -> instance ssh 
  // exec 
  private client: Client = new Client(); 
  private isConnected: boolean = false; 
 
  constructor(@Inject(SSH_OPTIONS) private options: SshModuleOptions) {} 
 
  connect(): Promise<any> { 
    if (this.isConnected)  { 
      return Promise.resolve();  
    } 
 
    return new Promise((resolve, reject) => { 
      const privateKey = this.options.privateKey  
        ? fs.readFileSync(this.options.privateKey)  
        : undefined; 
 
      const newOptions = { 
        ...this.options,  
        privateKey, 
      }; 
 
      this.client  
        .on('ready', () => { 
          this.isConnected  = true; 
          resolve(true); 
        }) 
        .on('error', (err) => { 
          reject(err); 
        }) 
        .connect(newOptions); 
    }); 
  } 
 
  exec(cmd: string): Promise<any> { 
    return new Promise(async (resolve, reject) => { 
      try { 
        await this.connect();  
        this.client.exec(cmd,  (err, stream) => { 
          if (err) { 
            reject(err); 
            return; 
          } 
 
          stream 
            .on('close', (code, signal) => { 
              // console.log(`STREAM:  close ${code}, ${signal}`); 
              resolve({ code, signal }); 
            }) 
            .on('data', (data) => { 
              // console.log(`STDOUT:  ${data}`); 
              resolve(data.toString());  
            }) 
            .stderr.on('data',  (data) => { 
              // console.log(`STDERR:  ${data}`); 
              reject(data.toString());  
            }); 
        }); 
      } catch (connectError) { 
        reject(connectError); 
      } 
    }); 
  } 
} 
  • 注意上面的 reject, 大多数情况下,服务端即便报错,也不要执行 reject
  • 因为 reject 之后,它会走到 nest 的全局过滤器, 从而调用 logger 来打印 error 日志
  • 这就不是我们期望的,我们期望 ssh 即便报错了,也要把报错信息反馈到 前端
  • 另外,.on('data') 产生的是 流数据, 流数据有的问题是会响应多条数据,所以,我们需要拼接一下
  • 我们需要暴露出来一个 callback 方法来调用 on data 和 on error
  • 下面来优化下 上面的 exec 方法
    exec(cmd: string, onData?:any): Promise<any> {
    	return new Promise(async (resolve, reject) => { 
    	  try { 
    	    await this.connect();  
    	    this.client.exec(cmd,  (err, stream) => { 
    	      if (err) { 
    	        reject(err); 
    	        return; 
    	      } 
    
    	      let output = '';
    	      stream 
    	        .on('close', (code, signal) => { 
    	          resolve({ code, signal, output }); 
    	        }) 
    	        .on('data', (data) => {
    	        	output += data.toString();
    	        	if (onData) onData(data.toString());
    	        })
    	        .stderr.on('data',  (data) => {
    	        	output += `ERROR: ${data.toString()}`;
    	        	if (onData) onData(`ERROR: ${data.toString()}`)
    	        }); 
    	    }); 
    	  } catch (connectError) { 
    	    reject(connectError); 
    	  } 
    	}); 
    } 
    
    • 这里对应上面 数据库备份滚动删除脚本中的 catch 相关注释
    • 有了上面的优化,可以不用 catch 了

8 )在某个模块上测试,配置 auth.module.ts

后续可能需要响应多台 主机的响应的扩展

import { Module } from '@nestjs/common'; 
import { AuthController } from './auth.controller';  
import { AuthService } from './auth.service';  
import { SshModule } from '@/utils/ssh/ssh.module'; 
 
@Module({ 
  imports: [ 
    SshModule.forRoot(
    	{
	       username: 'root', 
	       password: '123456', 
	       host: '192.168.31.77', 
	       port: 22, 
	    },
	    'ubuntu' // 这里是自定义的 name 
    ),
    SshModule.forRoot(
    	{
	       username: 'root', 
	       password: '123456', 
	       host: '192.168.31.77', 
	       port: 22, 
	    },
	    'ubuntu1' // 这里是自定义的 name 
    ),
    SshModule.forRootAsync(
    	{
	    	inject: [ConfigService],
	    	useFactory: (ConfigService) => {
	    		console.log('ConfigService: ', ConfigService.get('CRON_ON'));
	    		return {
	    			username: 'root',  // 这里可以使用 上面的 ConfigService 来动态的获取
			        password: '123456', // 同上
			        host: '192.168.31.77', // 同上
			        port: 22, // 同上
	    		}
	    	},
	    	'ubuntu3'
    })
  ],
  controllers: [AuthController], 
  providers: [AuthService, SshService], 
}) 
export class AuthModule {}
  • 上面用了不同 name 的 service

9 ) 在某个模块的控制器上使用, auth.controller.ts

import { Logger } from '@nestjs/common'; 
import { SshService } from '../../utils/ssh/ssh.service';

@Controller('auth')
export class AuthController {
  logger = new Logger();

  constructor(
  	@Inject('ubuntu:SSH') private sshService: SshService,
  	@Inject('ubuntu1:SSH') private sshService1: SshService,
  	@Inject('ubuntu3:SSH') private sshService2: SshService,
  ) {}

  @Post('/test')
  getTest(@Body('cmd') cmd: string) {
    return this.sshService.exec(cmd);
  }

  @Post('/test1')
  getTest1(@Body('cmd') cmd: string) {
    return this.sshService1.exec(cmd);
  }

  @Post('/testasync')
  getTest1(@Body('cmd') cmd: string) {
    return this.sshService2.exec(cmd);
  }
}
  • 这里就注入了不同的 service 的实例
  • 这样实例就更灵活和通用,可以连接不同的主机
NestJS 技术解析及实战指南 NestJS 是基于 Node.js 的渐进式服务端框架,使用 TypeScript 构建,融合了面向对象编程(OOP)、函数式编程(FP)和响应式编程(RP)理念。其核心设计借鉴了 Angular 的模块化思想,底层默认集成 Express(可切换为 Fastify),提供开箱即用的依赖注入、中间件管道、拦截器等企业级功能。​​核心特性示例:​​// 典型的模块定义@Module({imports: [DatabaseModule], // 依赖模块。 阅读详情

相关推荐

Nestjs框架: 番外篇之如何基于VsCode调试nodejs项目

本文介绍了如何在VSCode中配置Node.js项目的调试环境。步骤包括:1)点击左侧调试图标创建launch.json文件;2)选择Node.js调试器;3)删除默认配置后输入npm提示脚本;4)优化生成的配置脚本,修改"start:dev"命令、添加终端和控制台选项、指定Node.js版本;5)最后通过"Launch via NPM"启动调试。配置完成后可直接在VSCode中调试Node.js项目。(149字)

Wang的专栏 4745

nestjs从零到一,快速上手(一)

【代码】nestjs从零到一,快速上手。

ben_grf的博客 1466

nestjs研究

一个渐进式的框架,用于构建高效且可扩展的服务器端应用程序。 描述 框架TypeScript入门资料库。 安装 $ npm install 运行应用 # development $ npm run start # watch mode $ npm run start:dev # production mode $ npm run start:prod 测试 # unit tests $ npm run test # e2e tests $ npm run test:e2e # test coverage $ npm run test:cov 支持 Nest是MIT许可的开源项目。 得益于赞助商和出色支持者的支持,它可以发展壮大。 如果您想加入他们,请。 保持联系 作者 网站-https: 执照 Nest已。

NestJS学习之优秀项目分析与最佳实践

进入 NestJS 的世界可能会让你感到不知所措,尤其是当你面对众多的模块和概念时。本文不仅会深入分析优秀的 NestJS 项目,介绍常用的 Nest 内置模块,还会解锁一些 NestJS 的高级特性和最佳实践,来帮助你更好地理解和应用这个强大的 Node.js 框架。无论你是一个初学者还是有经验的开发者,这篇文章都将为你提供宝贵的见解和实用的技巧,让你能够更加了解 NestJS。我将从GitHub 上的 Awesome NestJS 列表。

cos的博客 1713

Cesium生态盘点:超图、火星3D等15个二次开发框架对比

本文深入解析Cesium生态中的15个二次开发框架,包括超图、火星3D等主流选择,帮助开发者根据项目需求进行精准选型。从性能表现、功能完备性、开发友好度等维度进行对比,特别适合需要处理地理空间数据和构建三维可视化的JavaScript开发者。

weixin_28197941的博客 499

别再手动算临界转速了!用ANSYS Workbench 2023 R2快速搞定转子坎贝尔图(附完整APDL命令流)

本文详细介绍了如何利用ANSYS Workbench 2023 R2快速生成转子坎贝尔图,实现全自动临界转速分析。通过对比传统手动计算与Workbench自动化流程的效率差异,展示了其在旋转机械设计中的革命性优势,包括大幅缩短分析周期、提升精度至98.7%。文章还提供了完整的APDL命令流和实战案例,帮助工程师快速掌握这一高效工具。

weixin_42699679的博客 475

Nest.js原理学习

nest.js原理

weixin_44663523的博客 996

SiamFC++笔记

SiamFC++: Towards Robust and Accurate Visual Tracking with Target Estimation Guidelines 文章根据这些思路:引入分类和目标状态估计分支(G1),无歧义的分类得分(G2),无先验知识的跟踪(G3)和评估质量得分(G4)来设计SiamFC ++。 SiamRPN ++和SiamFC ++的比较(红色部分代表得分高的区域): guide: G1 : 分类和状态估计任务的分离:分类任务将目标从干扰物和背景中分类出来,目标状态

木木JL的博客 9437

探索现代后端开发的瑰宝:Nest.js框架深度解析

探索现代后端开发的瑰宝:Nest.js框架深度解析 在当今快速发展的技术时代,构建高效且可扩展的服务器端应用已成为每个开发者追求的目标。而Nest.js,这个受到Angular启发的Node.js框架,正以它的进步性和强大的功能集,逐渐成为众多开发者的新宠儿。 项目介绍 Nest.js是一个专为构建高效、可扩展的服务器端应用而设计的渐进式Node.js框架。它巧妙地结合了TypeScript的强大...

gitblog_00284的博客 891

STM32中断实战:用ITR9606红外传感器DIY转速计数器(附完整代码)

本文详细介绍了如何使用STM32中断机制和ITR9606红外传感器DIY高精度转速计数器。从传感器选型、中断配置到抗干扰设计,提供完整的实战指南和优化代码,特别适合嵌入式开发者和硬件工程师快速实现工业级转速检测系统。

weixin_29159613的博客 293

C#图像处理:OpenCVSharp实现简单人脸识别

人脸识别是计算机视觉领域中的一个重要研究方向,它可以通过分析和比对人脸图像,实现识别和验证的功能。希望通过这篇文章的学习,你对OpenCVSharp和人脸识别有了初步的了解,并能够运用这些知识进行自己的项目开发。当我们运行程序时,它会加载人脸识别模型并读取待识别的图像,然后进行人脸检测并在图像上标记出检测到的人脸位置。当然,这只是一个入门级的示例,实际的人脸识别系统还需要更加复杂的算法和大量的训练数据来提高准确性和鲁棒性。最后,我们遍历检测到的人脸,并在原始图像上绘制矩形框,标记出检测到的人脸位置。

WELL_CODER的博客 1130

Pyppeteer高级应用:服务工作者拦截与移动端模拟实战

Pyppeteer是一个强大的无头Chrome/Chromium自动化库,作为Puppeteer的非官方Python端口,它允许开发者通过代码控制浏览器行为。本文将深入探讨Pyppeteer的两个高级应用场景:服务工作者(Service Worker)拦截网络请求和移动端设备模拟,帮助你构建更强大的Web自动化测试和数据采集工具。 ## 服务工作者拦截:掌控网络请求的终极指南 🚀 服务工作者

gitblog_00956的博客 961

在 SAP S/4HANA Public Cloud 中做产品扩展:从 Key User 到 ABAP Cloud 与 BTP 的三轨实践指南

摘要:SAP S/4HANA Public Cloud 提供三种扩展方式(key user、developer extensibility 和 side-by-side),遵循 clean core 原则确保升级稳定。key user 扩展适合无代码轻量改良,developer extensibility 用于受控 ABAP 开发,side-by-side 则适用于云原生独立应用。文章通过销售订单优先级字段、退货审批微服务和采购校验等案例,说明不同扩展路径的适用场景与实现方法,帮助开发者在三系统景观中合理选

2007 年 ~ 2025 年,深耕 SAP 技术 18 年 294

深入剖析一下 @nestjs/schedule 包在 NestJS 中实现定时任务的原理

@nestjs/schedule 将 Node.js 原生定时器和流行调度库的能力“NestJS 化”了,使其变得声明式、可注入、可管理,并完美融入 NestJS 的模块生命周期

pan303507479的博客 844

TripoSR: Fast 3D Object Reconstruction from a Single Image 论文阅读

模型的主要参数,如变换器的层数、三平面的维度、NeRF模型的具体细节和主要训练配置,在表1中详细说明。TripoSR的算法流程结合了先进的数据处理、模型设计和训练技术,使其能够在不到0.5秒的时间内从单张图像中快速生成详细的3D网格。总体而言,TripoSR是一个强大的3D重建工具,它在速度和质量上都表现出色,但也需要考虑到其对计算资源的需求和在处理极端复杂场景时可能遇到的挑战。:通过精心策划的训练数据集和多样化的数据渲染技术,TripoSR提高了模型的泛化能力和对真实世界图像的适应性。

Christo的博客 1949

国产化操作系统shell常用脚本

国产操作系统常用脚本:统信、麒麟。

Reyn_观极 599

Nestjs框架: nestjs-schedule模块中的三类定时任务

定时任务管理方案摘要 NestJS定时任务主要分为三类:周期性任务(如每日签到)、简单状态检查任务和轻量级任务。通过@nestjs/schedule模块实现,支持动态添加、删除和查询任务状态。典型实现包含任务服务类(管理任务生命周期)、定时服务类(具体业务逻辑)和模块整合。对于需要精确到分秒或高并发的任务(如消息推送),建议使用task queue方案(如Bull+Redis)。代码示例展示了MongoDB日志备份的定时任务实现,包括任务注册、执行和清理逻辑。该方案适用于中等规模系统,将定时任务集中管理在c

Wang的专栏 1528

精雕软件(JDpaint) 5.5 绿色版

精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版

nestjs[hello-world官方项目解析]

近几年由于 Node.js 的迅速发展,JavaScript 已经变成了一种全栈通用语言。前端方面诞生了一系列提高开发者生产力的优秀前端项目框架:诸如 Angular、React、Vue 等。而后端方面虽然有着诸如 Express,Koa,Fastify 等一系列优秀的开源库、工具,以及如eggjs、koa2等一些企业级的开发工具。但却缺乏真正意义上的框架。直到nestJS的出现。

墨水白云的博客 2509

锂电池充电器电路设计 里面有电路

锂电池充电器电路设计 里面有电路 3.7V升压到5V电路设计。

数学建模模型总结

这是本人在两年内准备参加数学建模竞赛搜集到的模型资料,已经经过了本人的筛选和整理,希望能够对准备参加数学建模竞赛的学弟学妹们提供一些帮助!

从Express到Nestjs,谈谈Nestjs的设计思想和使用方法

  最近已经使用过一段时间的nestjs,让人写着有一种java spring的感觉,nestjs可以使用express的所有中间件,此外完美的支持typescript,与数据库关系映射typeorm配合使用可以快速的编写一个接口网关。本文会介绍一下作为一款企业级的node框架的特点和优点。 从依赖注入(DI)谈起 装饰器和注解 nes...

weixin_34129696的博客 2348

LinePhone3.2.7(android端最新版本) sip客户端

SIP客户端For android:LinePhone3.2.7(最新版本),官网地址:http://www.linphone.org/

nestjs学习(一)

nest学习(一) 核心文件目录 src -app.controller.ts -app.module.ts -main.ts 控制器controller import { Controller, Get, Res, Req, HttpStatus } from '@nestjs/common'; @Controller() export clas...

AndyViky的博客 1960

nestjs利用定时任务发送邮件

邮件服务 安装邮件模块 yarn add @nestjs-modules/mailer nodemailer #or npm install --save @nestjs-modules/mailer nodemailer 安装模板引擎 yarn add handlebars #or yarn add pug #or yarn add ejs 新建邮件模块、控制器、服务 nest g module modules/email nest g co modules/email --no-spec nest

yw00yw的博客 3681

nestjs配置MySQL数据库_Nestjs操作mysql数据库_Nestjs typeOrm

Nestjs数据库Nest与数据库无关,允许您轻松地与任何SQL或NoSQL数据库集成。根据您的偏好,您有许多可用的选项。一般来说,将Nest连接到数据库只需为数据库加载一个适当的Node.js驱动程序,就像使用 Express 或Fastify一样。您还可以直接使用任何通用的Node.js数据库集成库或ORM,例如 Sequelize (recipe)、knexjs (tutorial)`和 T...

weixin_29149259的博客 1425

nestjs对mysql数据库的基本操作

一、配置基本步骤 1、安装基本的包 npm install --save @nestjs/typeorm typeorm mysql 2、安装一个环境包,区分开发环境与生产环境 npm install cross-env 3、在package.json包中配置环境 ... "scripts": { .... "start:dev": "cross-env NODE_EN...

水痕 9076

nodejs在windows下的安装配置(使用NVM的方式)

nodejs 是什么? Node.js® is a JavaScript runtime built on Chrome’s V8 JavaScript engine. Node.js uses an event-driven, non-blocking I/O model that makes it lightweight and efficient. Node.js’ package

Wang的专栏 6万+

nodejs笔记之:事件驱动,线程池,非阻塞,异常处理等

事件驱动:事件驱动的模型: 事件驱动的原理: 原理总结: Nodejs 会把所有请求和异步操作都放到一个事件队列中,用户的每一个请求就是一个事件。主线程先把普通代码执行完毕,然后会循环事件队列里的函数,如果遇到有IO的操作,nodejs会去线程池里拿出一个线程去执行IO的操作,执行完毕后再把拿到数据的回调函数,放到事件队列的尾部,继续事件循环。 线程池 线程池的概念:

Wang的专栏 6636

针对模块化,CommonJS规范,require原理和包查找机制等的整理

模块化的好处: 为了更有效的组织代码,提高重用性,增大开发效率,我们会把项目拆分成不同模块,每个模块,职责单一,多人协同,高效运行,易于维护。 模块的发展历程 js 不像其他高级语言有模块系统,标准库较少和更缺乏包管理系统。 js 起初只有全局对象的形式,通过一个个小函数来实现不同的模块功能 渐渐发展,通过构建对象的形式,来武装不同的功能 继续发展,通过立即执行函数和闭包的形式来分离一个又一个的小组

Wang的专栏 4286
上一篇: Nest框架: 日志功能之收集,筛选,存储,维护
下一篇: Nestjs框架: nestjs-schedule模块中的三类定时任务
Wang's Blog
博客等级 码龄12年 7438粉丝 2421原创
评论 3
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Wang's Blog

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值