npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@meta-1/nest-common

v0.0.39

Published

Common utilities and decorators for NestJS applications including caching, distributed lock, i18n, error handling, and more

Readme

@meta-1/nest-common

NestJS 应用的通用工具库,提供缓存、分布式锁、国际化、错误处理、事务管理等功能。

✨ 功能特性

  • 🎯 缓存装饰器 - 类似 Spring Boot 的 @Cacheable@CacheEvict 装饰器,支持 Redis,CacheEvict 支持 keys#{result} 占位符
  • 🔒 分布式锁 - @WithLock 装饰器,基于 Redis 实现分布式锁
  • ❄️ 雪花ID生成器 - @SnowflakeId 装饰器,自动生成分布式唯一ID
  • 🔄 事务管理 - @Transactional 装饰器,自动管理数据库事务
  • 🌍 国际化支持 - @I18n 装饰器和 I18nContext,支持命名空间和自动采集
  • 响应拦截器 - 统一的 API 响应格式
  • 🚨 错误处理 - 全局错误过滤器,支持自定义错误码
  • 📄 分页DTO - 标准化的分页请求和响应 DTO
  • 🌐 HTTP服务 - 增强的 HTTP 客户端,支持重试和文件下载
  • ⚙️ 配置加载器 - 支持从本地 YAML 文件或 Nacos 配置中心加载配置
  • 验证工具 - createI18nZodDto 自动采集验证错误消息
  • 📚 Swagger工具 - 分页响应的 Swagger Schema 生成工具
  • 🔤 I18n 收集器 - 开发环境自动收集缺失翻译键
  • 📂 Locales 同步 - syncLocales 同步翻译文件到 i18n 目录
  • 🗄️ 迁移 CLI - runMigration 可配置的 TypeORM 多模块迁移

📦 安装

npm install @meta-1/nest-common
# 或
pnpm add @meta-1/nest-common
# 或
yarn add @meta-1/nest-common

peer 依赖

npm install @nestjs/common @nestjs/platform-express @nestjs-modules/ioredis nestjs-i18n nestjs-zod ioredis lodash nacos yaml zod

说明: 缓存和分布式锁依赖 @nestjs-modules/ioredis,需先配置 RedisModule

🚀 使用指南

1. 模块导入

import { CommonModule } from '@meta-1/nest-common';

@Module({
  imports: [CommonModule],
})
export class AppModule {}

CommonModule 会自动注册以下全局功能:

  • ResponseInterceptor - 统一响应格式
  • ErrorsFilter - 全局错误处理
  • ZodValidationPipe - Zod 验证管道
  • HttpService - HTTP 客户端服务

2. 缓存装饰器

初始化

缓存功能依赖 @nestjs-modules/ioredis,需在应用模块中导入 RedisModule

import { RedisModule } from '@nestjs-modules/ioredis';

@Module({
  imports: [
    RedisModule.forRoot({
      type: 'single',
      url: 'redis://localhost:6379',
    }),
    CommonModule,
  ],
})
export class AppModule {}

CommonModule 内置 CacheableInitializer,会自动发现所有使用 @CacheableService() 的类并注入 Redis。

使用示例

import { CacheableService, Cacheable, CacheEvict } from '@meta-1/nest-common';

@CacheableService()
@Injectable()
export class UserService {
  // 缓存结果,默认 TTL 300 秒
  @Cacheable({ key: 'user:#{0}', ttl: 300 })
  async getUserById(id: string) {
    return await this.userRepository.findOne({ where: { id } });
  }

  // 使用对象属性作为缓存键
  @Cacheable({ key: 'user:#{user.id}:profile', ttl: 600 })
  async getUserProfile(user: { id: string }) {
    return await this.userRepository.findProfile(user.id);
  }

  // 清除单个缓存
  @CacheEvict({ key: 'user:#{0}' })
  async updateUser(id: string, data: UpdateUserDto) {
    return await this.userRepository.update(id, data);
  }

  // 清除多个缓存(支持从返回值读取)
  @CacheEvict({
    keys: ['user:#{0}', 'user:#{result.id}:profile', 'user:#{result.channel}:#{result.username}'],
  })
  async updateUserWithEvict(id: string, data: UpdateUserDto) {
    const user = await this.userRepository.update(id, data);
    return user; // #{result.id} 等从返回值读取
  }
}

缓存键占位符:

  • #{0}, #{1}, #{2} - 使用参数位置索引
  • #{user.id}, #{name} - 使用第一个参数的属性(等同于 #{0.user.id}
  • #{1.book.title} - 使用指定参数的路径属性
  • #{result}, #{result.id} - 从方法返回值读取(仅 @CacheEvictkeys 选项)

缓存键前缀: 键名会自动添加 cache: 前缀(若已以 cache: 开头则不会重复添加)

3. 分布式锁装饰器

初始化

分布式锁同样依赖 RedisModule,与缓存共用同一 Redis 实例。确保已按「缓存装饰器」章节配置 RedisModule 即可,LockInitializer 会自动发现并注入。

锁键前缀: 键名会自动添加 lock: 前缀(若已以 lock: 开头则不会重复添加)

使用示例

import { WithLock } from '@meta-1/nest-common';

@Injectable()
export class OrderService {
  // 防止同一用户重复创建订单
  @WithLock({
    key: 'order:create:#{0}',
    ttl: 10000,        // 锁过期时间:10秒
    waitTimeout: 3000, // 等待锁的超时时间:3秒
  })
  async createOrder(userId: string, items: OrderItem[]) {
    // 同一用户的订单创建操作会被加锁
    return await this.orderRepository.save({ userId, items });
  }

  // 防止重复支付
  @WithLock({
    key: 'payment:#{0}',
    ttl: 30000,
    waitTimeout: 0, // 不等待,立即失败
    errorMessage: '订单正在支付中,请勿重复提交',
  })
  async processPayment(orderId: string) {
    // 支付逻辑
  }

  // 使用对象属性作为锁键
  @WithLock({
    key: 'inventory:#{product.id}',
    ttl: 5000,
  })
  async reduceInventory(product: { id: string; quantity: number }) {
    // 库存扣减逻辑
  }
}

配置选项:

| 参数 | 类型 | 默认值 | 说明 | |------|------|--------|------| | key | string | 必填 | 锁的键名,支持占位符 | | ttl | number | 30000 | 锁的过期时间(毫秒) | | waitTimeout | number | 5000 | 等待锁的超时时间(毫秒),0 表示不等待 | | retryInterval | number | 100 | 重试获取锁的间隔(毫秒) | | errorMessage | string | '操作正在处理中,请稍后重试' | 获取锁失败时的错误提示 |

4. 雪花ID生成器

import { SnowflakeId } from '@meta-1/nest-common';
import { Entity, Column } from 'typeorm';

@Entity()
export class User {
  @SnowflakeId()
  id: string; // 自动生成分布式唯一ID

  @Column()
  name: string;
}

// 使用时无需手动设置 ID
const user = new User();
user.name = 'Alice';
await repository.save(user); // ID 自动生成

环境变量配置:

SNOWFLAKE_WORKER_ID=0      # 0-31
SNOWFLAKE_DATACENTER_ID=0 # 0-31

特性:

  • 并发安全:通过 Promise 链确保多实例同时插入时 ID 不重复
  • 分布式唯一:支持多机部署
  • 时间有序:ID 按生成时间递增
  • 高性能:单机每毫秒可生成 4096 个唯一 ID
  • 时间回拨保护:检测并拒绝时钟回拨
  • 自定义起始时间:epoch 为 2021-01-01 00:00:00 UTC

5. 事务装饰器

import { Transactional } from '@meta-1/nest-common';

@Injectable()
export class OrderService {
  constructor(
    @InjectRepository(Order) private orderRepo: Repository<Order>,
    @InjectRepository(OrderItem) private itemRepo: Repository<OrderItem>,
  ) {}

  @Transactional()
  async createOrder(orderData: CreateOrderDto) {
    // 所有数据库操作都在同一个事务中
    const order = await this.orderRepo.save(orderData);
    
    for (const item of orderData.items) {
      await this.itemRepo.save({ ...item, orderId: order.id });
    }
    
    // 如果任何操作失败,整个事务会自动回滚
    return order;
  }
}

注意事项:

  • Service 中必须注入至少一个 Repository
  • 方法内抛出的任何错误都会触发回滚
  • 只有方法正常返回时事务才会提交

6. 国际化支持

设置

import { I18nModule } from 'nestjs-i18n';
import * as path from 'path';

@Module({
  imports: [
    I18nModule.forRoot({
      fallbackLanguage: 'en',
      loaderOptions: {
        path: path.join(__dirname, '/i18n/'),
        watch: true,
      },
    }),
  ],
})
export class AppModule {}

使用示例

import { I18n, I18nContext } from '@meta-1/nest-common';

@Controller('users')
export class UserController {
  @Get()
  async getUsers(@I18n() i18n: I18nContext) {
    const users = await this.userService.findAll();
    
    return {
      message: i18n.t('users.list.success'), // 自动添加 'common.' 前缀
      data: users,
    };
  }

  @Post()
  async createUser(
    @Body() dto: CreateUserDto,
    @I18n() i18n: I18nContext,
  ) {
    const user = await this.userService.create(dto);
    
    return {
      message: i18n.t('users.create.success', {
        args: { name: user.name },
      }),
      data: user,
    };
  }
}

自定义命名空间:

import { createI18nContext, type RawI18nContext } from '@meta-1/nest-common';
import { I18n as NestI18n } from 'nestjs-i18n';

@Controller('products')
export class ProductController {
  @Get()
  async getProducts(@NestI18n() rawI18n: RawI18nContext) {
    const i18n = createI18nContext(rawI18n, 'products');
    
    return {
      message: i18n.t('list.success'), // 翻译为 'products.list.success'
      data: await this.productService.findAll(),
    };
  }
}

7. 错误处理

定义错误码

import { defineErrorCode, AppError } from '@meta-1/nest-common';

// 定义模块错误码
export const UserErrorCode = defineErrorCode({
  USER_NOT_FOUND: { code: 2000, message: 'User not found' },
  USER_ALREADY_EXISTS: { code: 2001, message: 'User already exists' },
});

// 使用
@Injectable()
export class UserService {
  async getUserById(id: string) {
    const user = await this.userRepository.findOne({ where: { id } });
    
    if (!user) {
      throw new AppError(UserErrorCode.USER_NOT_FOUND, { userId: id });
    }
    
    return user;
  }
}

错误码范围约定:

  • 0-999: 通用错误(@meta-1/nest-common
  • 1000-1999: Message 模块错误
  • 2000-2999: User 模块错误
  • 3000-3999: Auth 模块错误
  • 100-199: 分布式锁错误

错误响应格式:

{
  "code": 2000,
  "success": false,
  "message": "User not found",
  "data": { "userId": "123" },
  "timestamp": "2024-01-01T00:00:00.000Z",
  "path": "/api/users/123"
}

8. 分页DTO

import { PageRequestDto, PageDataDto } from '@meta-1/nest-common';
import { ApiOkResponse } from '@nestjs/swagger';
import { createPageSchema, createPageModels } from '@meta-1/nest-common';

@Controller('users')
export class UserController {
  @Get()
  @ApiOkResponse({
    schema: createPageSchema(UserDto),
  })
  @ApiExtraModels(...createPageModels(UserDto))
  async getUsers(@Query() query: PageRequestDto) {
    const [data, total] = await this.userService.findAndCount(query);
    
    return PageDataDto.of(total, data);
  }
}

9. HTTP服务

import { HttpService } from '@meta-1/nest-common';

@Injectable()
export class ExternalApiService {
  constructor(private readonly httpService: HttpService) {}

  async fetchData() {
    // GET 请求
    const response = await this.httpService.get<Data>('https://api.example.com/data');
    return response.data;
  }

  async postData(data: any) {
    // POST 请求(支持重试)
    const response = await this.httpService.post<Result>('https://api.example.com/data', data, {
      retries: 3,
      retryDelay: 1000,
    });
    return response.data;
  }

  async downloadFile(url: string, filePath: string) {
    // 下载文件(支持进度回调)
    await this.httpService.download({
      url,
      filePath,
      onProgress: (progress) => {
        console.log(`下载进度: ${progress}%`);
      },
    });
  }
}

10. 配置加载器

import { ConfigLoader, ConfigSourceType } from '@meta-1/nest-common';

// 从本地 YAML 文件加载
const loader = new ConfigLoader<AppConfig>({
  type: ConfigSourceType.LOCAL_YAML,
  filePath: './config/app.yaml',
});
const config = await loader.load();

// 从 Nacos 加载
const nacosLoader = new ConfigLoader<AppConfig>({
  type: ConfigSourceType.NACOS,
  server: '127.0.0.1:8848',
  dataId: 'app-config',
  group: 'DEFAULT_GROUP',
  namespace: 'public',
  username: 'nacos',
  password: 'nacos',
});
const nacosConfig = await nacosLoader.load();

配置特性:

  • 自动将 kebab-case 键名转换为 camelCase
  • 支持 YAML 格式
  • 支持 Nacos 配置中心

11. 验证工具

import { createI18nZodDto } from '@meta-1/nest-common';
import { z } from 'zod';

const CreateUserSchema = z.object({
  email: z.string().email('Invalid email format'),
  password: z.string().min(8, 'Password must be at least 8 characters'),
});

export class CreateUserDto extends createI18nZodDto(CreateUserSchema) {}

特性:

  • 自动采集 Schema 中的所有验证错误消息到 i18n collector
  • 支持开发环境自动收集翻译 key

12. Swagger工具

import { createPageSchema, createPageModels } from '@meta-1/nest-common';
import { ApiOkResponse, ApiExtraModels } from '@nestjs/swagger';

@Controller('users')
export class UserController {
  @Get()
  @ApiOkResponse({
    schema: createPageSchema(UserDto),
  })
  @ApiExtraModels(...createPageModels(UserDto))
  async getUsers() {
    // ...
  }
}

13. 工具函数

import { generateKey, md5, PlainTextLogger } from '@meta-1/nest-common';

// 生成动态键名(参数)
const key = generateKey('user:#{0}:profile:#{1.name}', ['123', { name: 'Alice' }]);
// 结果: 'user:123:profile:Alice'

// 从返回值生成键名(用于 CacheEvict 的 keys)
const evictKey = generateKey('user:#{result.id}', [], { id: '456' });
// 结果: 'user:456'

// MD5 哈希
const hash = md5('hello world');

// TypeORM 纯文本日志输出器
const logger = new PlainTextLogger();

14. I18n 缺失键收集器

开发环境下自动收集缺失的翻译键并写入 locales 文件:

import * as path from 'path';
import { initI18nCollector, getI18nCollector } from '@meta-1/nest-common';
import { I18nModule } from 'nestjs-i18n';

// 1. 在应用启动时先初始化收集器(仅开发环境)
if (process.env.NODE_ENV === 'development') {
  const localesDir = path.join(process.cwd(), 'locales');
  initI18nCollector(localesDir);
}

// 2. 配置 I18nModule 时传入 missingKeyHandler
I18nModule.forRoot({
  // ... 其他配置
  missingKeyHandler: (key: string) => {
    const collector = getI18nCollector();
    if (collector) {
      const actualKey = key.includes('.') ? key.split('.').slice(1).join('.') : key;
      collector.add(actualKey);
    }
  },
});

15. Locales 同步工具

locales/*.json 同步到 i18n 目标目录:

import { syncLocales } from '@meta-1/nest-common';

// 一次性同步
syncLocales({
  sourceDir: './locales',
  targetDir: './dist/apps/server/i18n',
});

// 监听模式(开发时)
syncLocales({
  sourceDir: './locales',
  targetDir: './dist/apps/server/i18n',
  watch: true,
});

16. TypeORM 迁移 CLI

可配置的迁移脚本,支持多模块:

import { runMigration } from '@meta-1/nest-common';

runMigration({
  libsPath: path.join(process.cwd(), 'libs'),
  dataSourcePath: path.join(process.cwd(), 'apps/server/src/data-source.ts'),
});

用法:

  • migration:generate <模块名> <迁移名称> - 生成迁移
  • migration:create <模块名> <迁移名称> - 创建空迁移
  • migration:run - 执行所有待运行迁移
  • migration:revert - 回滚最后一次迁移
  • migration:show - 显示迁移状态

📝 API 参考

装饰器

  • @CacheableService() - 标记服务类支持缓存
  • @Cacheable(options) - 缓存方法结果
  • @CacheEvict(options) - 清除缓存
  • @WithLock(options) - 分布式锁
  • @SnowflakeId() - 自动生成雪花ID
  • @Transactional() - 自动事务管理
  • @I18n() - 注入 I18nContext

  • CommonModule - 通用模块
  • AppError - 自定义错误类
  • I18nContext - 国际化上下文
  • ErrorsFilter - 全局异常过滤器
  • ResponseInterceptor - 响应拦截器
  • HttpService - HTTP 客户端服务
  • PageRequestDto - 分页请求 DTO
  • PageDataDto<T> - 分页响应 DTO
  • ConfigLoader<T> - 配置加载器
  • PlainTextLogger - TypeORM 纯文本日志输出器

函数

  • defineErrorCode(definition) - 定义错误码
  • createI18nZodDto(schema) - 创建支持 i18n 的 Zod DTO
  • createI18nContext(context, namespace) - 创建自定义命名空间上下文
  • createPageSchema(itemDto) - 创建分页 Swagger Schema
  • createPageModels(itemDto) - 创建分页 Swagger Models
  • generateKey(pattern, args, result?) - 生成动态键名,result 用于 #{result} 占位符
  • md5(text) - MD5 哈希
  • initI18nCollector(localesDir) - 初始化 I18n 缺失键收集器(仅开发环境)
  • getI18nCollector() - 获取 I18n 收集器实例
  • syncLocales(options) - 同步 locales 文件到 i18n 目录
  • runMigration(config) - 执行 TypeORM 迁移 CLI

错误码

通用错误码 (0-999):

  • SERVER_ERROR (500) - 服务器错误
  • VALIDATION_FAILED (400) - 验证失败
  • UNAUTHORIZED (401) - 未授权
  • FORBIDDEN (403) - 禁止访问
  • NOT_FOUND (404) - 未找到
  • I18N_CONTEXT_NOT_FOUND (500) - I18n 上下文未找到
  • CONFIG_NOT_FOUND (3000) - 配置未找到
  • CONFIG_INVALID (3001) - 配置无效

分布式锁错误码 (100-199):

  • REDIS_NOT_INJECTED (100) - Redis 未注入
  • LOCK_ACQUIRE_FAILED (110) - 获取锁失败
  • LOCK_ACQUIRE_ERROR (111) - 获取锁时发生错误
  • LOCK_RELEASE_ERROR (112) - 释放锁时发生错误

📄 License

MIT