You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

NestJs Serverless架构下SendGrid.setApiKey is not a function错误求助

问题描述

本地运行NestJS的sendgrid.service.ts时,SendGrid.setApiKey可正常设置密钥并发送邮件,但部署到AWS Lambda及Vercel时,均出现SendGrid.setApiKey is not a function错误。此前曾遇到bcrypt包在无服务器环境的类似问题,目前已卡数日,寻求解决建议。

相关代码配置

sendgrid.service.ts (路径: src/auth/services)

import { Injectable } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import * as SendGrid from '@sendgrid/mail';

@Injectable()
export class SendgridService {
    constructor() {  
      SendGrid.setApiKey(process.env.SEND_GRID_KEY);
    }

    async send(mail: SendGrid.MailDataRequired) {
        const transport = await SendGrid.send(mail);
        return transport;
    }
}

package.json

{
    "name": "api",
    "version": "0.0.1",
    "description": "",
    "author": "",
    "private": true,
    "license": "UNLICENSED",
    "scripts": {
        "prebuild": "rimraf dist",
        "build": "nest build",
        "format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"",
        "start": "nest start",
        "start:dev": "nest start --watch",
        "start:debug": "nest start --debug --watch",
        "start:prod": "node dist/main",
        "lint": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix",
        "test": "jest",
        "test:watch": "jest --watch",
        "test:cov": "jest --coverage",
        "test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand",
        "test:e2e": "jest --config ./test/jest-e2e.json"
    },
    "dependencies": {
        "@nestjs/common": "^9.4.0",
        "@nestjs/config": "^2.3.1",
        "@nestjs/core": "^9.4.0",
        "@nestjs/jwt": "^10.0.3",
        "@nestjs/passport": "^9.0.3",
        "@nestjs/platform-express": "^9.4.0",
        "@nestjs/platform-socket.io": "^9.4.0",
        "@nestjs/schedule": "^2.2.1",
        "@nestjs/typeorm": "^9.0.1",
        "@nestjs/websockets": "^9.4.0",
        "@sendgrid/mail": "^7.7.0",
        "@vendia/serverless-express": "^4.10.1",
        "aws-lambda": "^1.0.7",
        "aws-sdk": "^2.1362.0",
        "bcryptjs": "^2.4.3",
        "class-transformer": "^0.5.1",
        "class-validator": "^0.14.0",
        "cookie-parser": "^1.4.6",
        "dotenv": "^16.0.3",
        "file-type": "^18.2.1",
        "jimp": "^0.22.7",
        "morgan": "^1.10.0",
        "nodemailer": "^6.9.1",
        "passport": "^0.6.0",
        "passport-jwt": "^4.0.1",
        "passport-local": "^1.0.0",
        "pg": "^8.10.0",
        "postmark": "^3.0.15",
        "reflect-metadata": "^0.1.13",
        "rxjs": "^7.8.0",
        "sharp": "^0.32.0",
        "socket.io": "^4.6.1",
        "stripe": "^12.2.0",
        "typeorm": "^0.3.15",
        "uuid": "^9.0.0"
    },
    "devDependencies": {
        "@nestjs/cli": "^9.4.0",
        "@nestjs/schematics": "^9.1.0",
        "@nestjs/testing": "^9.4.0",
        "@types/aws-lambda": "^8.10.114",
        "@types/express": "^4.17.17",
        "@types/jest": "^29.5.1",
        "@types/multer": "^1.4.7",
        "@types/node": "^18.15.12",
        "@types/passport-jwt": "^3.0.8",
        "@types/supertest": "^2.0.12",
        "@typescript-eslint/eslint-plugin": "^5.59.0",
        "@typescript-eslint/parser": "^5.59.0",
        "eslint": "^8.38.0",
        "eslint-config-prettier": "^8.8.0",
        "eslint-plugin-prettier": "^4.2.1",
        "jest": "^29.5.0",
        "node-mocks-http": "^1.12.2",
        "prettier": "^2.8.7",
        "rimraf": "^5.0.0",
        "serverless-offline": "^12.0.4",
        "supertest": "^6.3.3",
        "ts-jest": "^29.1.0",
        "ts-loader": "^9.4.2",
        "ts-node": "^10.9.1",
        "tsconfig-paths": "^4.2.0",
        "typescript": "^5.0.4"
    },
    "jest": {
        "moduleFileExtensions": [
            "js",
            "json",
            "ts"
        ],
        "rootDir": "src",
        "testRegex": ".*\\.spec\\.ts$",
        "transform": {
            "^.+\\.(t|j)s$": "ts-jest"
        },
        "collectCoverageFrom": [
            "**/*.(t|j)s"
        ],
        "coverageDirectory": "../coverage",
        "testEnvironment": "node"
    }
}

tsconfig.json

{
    "compilerOptions": {
        "module": "commonjs",
        "declaration": true,
        "removeComments": true,
        "emitDecoratorMetadata": true,
        "experimentalDecorators": true,
        "allowSyntheticDefaultImports": true,
        "target": "es2017",
        "sourceMap": true,
        "outDir": "./dist",
        "baseUrl": "./",
        "incremental": true,
        "skipLibCheck": true,
        "strictNullChecks": false,
        "noImplicitAny": false,
        "strictBindCallApply": false,
        "forceConsistentCasingInFileNames": false,
        "noFallthroughCasesInSwitch": false,
        "esModuleInterop": true
    }
}

serverless.yaml

service: SERVICE-NAME

useDotenv: true

plugins:
    - serverless-offline

provider:
    name: aws
    runtime: nodejs12.x
    environment:
        POSTGRES_HOST: ${env:POSTGRES_HOST}
        PORT_OF_POSTGRES: ${env:PORT_OF_POSTGRES}
        POSTGRES_USER: ${env:POSTGRES_USER}
        POSTGRES_PASSWORD: ${env:POSTGRES_PASSWORD}
        POSTGRES_DATABASE: ${env:POSTGRES_DATABASE}
        JWT_SECRET: ${env:JWT_SECRET}
        TIMEZONE: ${env:TIMEZONE}
        ACCESS_KEY_ID: ${env:ACCESS_KEY_ID}
        SECRET_ACCESS_KEY: ${env:SECRET_ACCESS_KEY}
        BUCKET_NAME_FOR_SUMMARY: ${env:BUCKET_NAME_FOR_SUMMARY}
        SEND_GRID_KEY: ${env:SEND_GRID_KEY}
        STRIPE_SECRET_KEY: ${env:STRIPE_SECRET_KEY}
        STRIPE_PUBLIC_KEY: ${env:STRIPE_PUBLIC_KEY}

functions:
    main:
        handler: dist/serverless.handler
        events:
            - http:
                method: ANY
                path: /
            - http:
                method: ANY
                path: '{proxy+'}
解决建议
  • 调整SendGrid导入方式:将命名空间导入改为默认导入,适配无服务器环境的模块解析逻辑
    修改sendgrid.service.ts中的导入代码:

    import SendGrid from '@sendgrid/mail';
    

    无服务器环境对ES模块和CommonJS模块的混合解析规则与本地Node环境存在差异,默认导入能避免模块导出结构异常。

  • 升级AWS Lambda运行时版本:当前配置的nodejs12.x已停止维护,对现代模块的支持不完善,将serverless.yaml中的运行时改为nodejs18.x(或更高LTS版本):

    provider:
      name: aws
      runtime: nodejs18.x
    
  • 延迟API密钥初始化:将API密钥的设置从构造函数移到send方法中,避免冷启动时模块加载顺序问题:

    import { Injectable } from '@nestjs/common';
    import { ConfigService } from '@nestjs/config';
    import SendGrid from '@sendgrid/mail';
    
    @Injectable()
    export class SendgridService {
        constructor(private configService: ConfigService) {}
    
        async send(mail: typeof SendGrid.MailDataRequired) {
            SendGrid.setApiKey(this.configService.get('SEND_GRID_KEY'));
            return await SendGrid.send(mail);
        }
    }
    

    同时注入ConfigService来获取环境变量,比直接读取process.env更符合NestJS的最佳实践。

  • 验证依赖兼容性:

    1. 部署前执行npm install --production安装生产依赖,避免开发依赖干扰
    2. 将@sendgrid/mail升级到最新稳定版:npm install @sendgrid/mail@latest,确保与新版Node环境兼容

内容的提问来源于stack exchange,提问作者Zeke John

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.24 15:35:23