NestJS依赖注入错误:MailAppService依赖无法解析(适配器模式场景)
NestJS 适配器模式整合 SendGrid 与 Nodemailer 依赖注入错误
错误信息
ERROR [ExceptionHandler] Nest can't resolve dependencies of the MailAppService (?). 请确保索引[0]处的对象在MailModule上下文中可用。
问题原因
MailAppService 构造函数依赖的 MailServiceInterface 是 TypeScript 接口,编译后会被完全移除,NestJS 的依赖注入系统无法识别这种“仅编译时存在”的类型。当前代码虽有 NodemailerAdapterService 和 SendGridAdapterService 实现了该接口,但未在模块中配置接口与实现类的绑定关系,导致 Nest 找不到对应实例。
解决方案
通过自定义提供者将接口与具体实现绑定,让 Nest 明确注入目标。以下是分步实现方案:
1. 创建接口令牌
为避免接口编译后丢失类型标识,定义一个注入令牌:
// src/modules/mail/interface/mail-service.interface.ts import { InjectionToken } from '@nestjs/common'; export const MAIL_SERVICE = new InjectionToken<MailServiceInterface>('MAIL_SERVICE'); export interface MailServiceInterface { sendWelcomeEmail(email: string, name: string): Promise<void>; sendAccountDeletionEmail(email: string, name: string): Promise<void>; }
2. 修改 MailAppService 的注入方式
使用 @Inject() 装饰器指定注入令牌:
// src/modules/mail/service/mail.service.ts import { Injectable, Inject } from '@nestjs/common'; import { MailServiceInterface, MAIL_SERVICE } from '../interface/mail-service.interface'; @Injectable() export class MailAppService { constructor(@Inject(MAIL_SERVICE) private readonly emailInterface: MailServiceInterface) {} async sendWelcomeEmail(email: string, name: string) { await this.emailInterface.sendWelcomeEmail(email, name); } async sendAccountDeletionEmail(email: string, name: string) { await this.emailInterface.sendAccountDeletionEmail(email, name); } }
3. 在 MailModule 中配置绑定
根据需求选择固定绑定或动态切换适配器:
方式1:固定绑定到某一适配器
// src/modules/mail/mail.module.ts import { Module } from '@nestjs/common'; import { SendGridService } from './service/sendGrid.service'; import { MailerModule } from '@nestjs-modules/mailer'; import { envConfigurations } from '../../../env/env.configuration'; import { HandlebarsAdapter } from '@nestjs-modules/mailer/dist/adapters/handlebars.adapter'; import { MailAppService } from './service/mail.service'; import { NodemailerService } from './service/nodemailer.service'; import { NodemailerAdapterService } from './service/nodemailer-adapter.service'; import { SendGridAdapterService } from './service/sendGrid-adapter.service'; import { MAIL_SERVICE } from './interface/mail-service.interface'; @Module({ imports: [ MailerModule.forRoot({ transport: { host: envConfigurations().mail.nodemailer.host, port: envConfigurations().mail.nodemailer.port, secure: envConfigurations().mail.nodemailer.secure, service: envConfigurations().mail.nodemailer.service, auth: { user: envConfigurations().mail.nodemailer.auth.user, pass: envConfigurations().mail.nodemailer.auth.pass, }, }, defaults: { from: `"No Reply" - <${envConfigurations().mail.from}>`, }, template: { dir: process.cwd() + '/src/modules/mail/templates/', adapter: new HandlebarsAdapter(), options: { strict: true, }, }, }), ], providers: [ MailAppService, NodemailerService, NodemailerAdapterService, SendGridAdapterService, SendGridService, // 绑定接口令牌到具体实现 { provide: MAIL_SERVICE, useClass: NodemailerAdapterService, // 可替换为 SendGridAdapterService }, ], controllers: [], exports: [MailAppService], }) export class MailModule {}
方式2:根据环境变量动态切换
// src/modules/mail/mail.module.ts import { Module } from '@nestjs/common'; import { SendGridService } from './service/sendGrid.service'; import { MailerModule } from '@nestjs-modules/mailer'; import { envConfigurations } from '../../../env/env.configuration'; import { HandlebarsAdapter } from '@nestjs-modules/mailer/dist/adapters/handlebars.adapter'; import { MailAppService } from './service/mail.service'; import { NodemailerService } from './service/nodemailer.service'; import { NodemailerAdapterService } from './service/nodemailer-adapter.service'; import { SendGridAdapterService } from './service/sendGrid-adapter.service'; import { MAIL_SERVICE } from './interface/mail-service.interface'; @Module({ imports: [ MailerModule.forRoot({ transport: { host: envConfigurations().mail.nodemailer.host, port: envConfigurations().mail.nodemailer.port, secure: envConfigurations().mail.nodemailer.secure, service: envConfigurations().mail.nodemailer.service, auth: { user: envConfigurations().mail.nodemailer.auth.user, pass: envConfigurations().mail.nodemailer.auth.pass, }, }, defaults: { from: `"No Reply" - <${envConfigurations().mail.from}>`, }, template: { dir: process.cwd() + '/src/modules/mail/templates/', adapter: new HandlebarsAdapter(), options: { strict: true, }, }, }), ], providers: [ MailAppService, NodemailerService, NodemailerAdapterService, SendGridAdapterService, SendGridService, // 动态选择适配器 { provide: MAIL_SERVICE, useFactory: () => { const config = envConfigurations(); if (config.mail.defaultService === 'sendgrid') { return new SendGridAdapterService(new SendGridService()); } else { return new NodemailerAdapterService(new NodemailerService()); } }, }, ], controllers: [], exports: [MailAppService], }) export class MailModule {}
补充注意事项
- 确保所有服务类都已添加到
MailModule的providers数组; - 若需在其他模块使用
MailAppService,需确保MailModule的exports包含该服务; - 动态切换时,需在环境变量中配置
MAIL_DEFAULT_SERVICE(如.env文件添加MAIL_DEFAULT_SERVICE=sendgrid)。
内容的提问来源于stack exchange,提问作者WMF
相关产品推荐
相关产品推荐

