如何通过Swagger+Node.js Lambda编程配置AWS API Gateway?
我来帮你搞定这个问题!之前手动在AWS控制台操作确实繁琐,用Node.js Lambda自动化完全可行——核心就是调用AWS SDK的API Gateway和Cognito相关接口,下面一步步给你拆解实现步骤:
我们要通过Lambda完成三个核心动作:
- 导入Swagger/OpenAPI定义创建API Gateway实例
- 创建并配置Cognito用户池授权器
- 将授权器关联到API的端点上,最后部署API让它可用
Lambda需要能操作API Gateway和Cognito的权限,你得给Lambda的执行角色附加以下权限策略(可以直接在IAM控制台创建自定义策略):
{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "apigateway:ImportRestApi", "apigateway:CreateAuthorizer", "apigateway:GetResources", "apigateway:PutMethod", "apigateway:CreateDeployment", "cognito-idp:DescribeUserPool" ], "Resource": "*" } ] }
提示:如果你的资源范围明确,可以把Resource改成具体的ARN,比如特定的API Gateway或Cognito用户池,更安全。
推荐使用AWS SDK for JavaScript v3(模块化,体积小,适合Lambda),先在你的项目里安装API Gateway客户端:
npm install @aws-sdk/client-api-gateway
然后编写导入Swagger的函数,这里假设你的Swagger定义存在Lambda的环境变量里(也可以从S3读取):
const { APIGatewayClient, ImportRestApiCommand, GetResourcesCommand } = require("@aws-sdk/client-api-gateway"); // 初始化API Gateway客户端 const apiGatewayClient = new APIGatewayClient({ region: process.env.AWS_REGION }); async function importApiFromSwagger() { // 从环境变量读取Swagger JSON字符串并解析 const swaggerDefinition = JSON.parse(process.env.SWAGGER_DEFINITION); const importParams = { body: swaggerDefinition, failOnWarnings: false // 允许Swagger存在警告,根据你的需求调整 }; const command = new ImportRestApiCommand(importParams); const response = await apiGatewayClient.send(command); // 返回创建的API的ID,后续步骤要用 return response.id; }
如果你的Swagger里已经定义了所有端点,导入后这些端点就自动存在了;如果需要额外添加自定义端点,后面会讲怎么操作。
接下来创建关联到你Cognito用户池的授权器,代码如下:
async function createCognitoAuthorizer(apiId) { const createParams = { restApiId: apiId, name: "MyCognitoAuthorizer", // 自定义授权器名称 type: "COGNITO_USER_POOLS", providerARNs: [ `arn:aws:cognito-idp:${process.env.AWS_REGION}:${process.env.AWS_ACCOUNT_ID}:userpool/${process.env.COGNITO_USER_POOL_ID}` ], identitySource: "method.request.header.Authorization", // 从Authorization头获取JWT token authorizerResultTtlInSeconds: 300 // 授权结果缓存时间,可选 }; const command = new CreateAuthorizerCommand(createParams); const response = await apiGatewayClient.send(command); return response.id; // 返回授权器ID,后续关联端点要用 }
注意:这里的环境变量COGNITO_USER_POOL_ID、AWS_ACCOUNT_ID需要提前在Lambda控制台配置好。
如果你的Swagger里没配置授权,需要手动给每个端点方法添加授权。首先得获取API的资源ID(比如根路径/的ID,或者你自定义端点的资源ID),然后调用PutMethodCommand更新方法的授权配置:
const { PutMethodCommand } = require("@aws-sdk/client-api-gateway"); async function attachAuthorizerToEndpoint(apiId, authorizerId, httpMethod, resourcePath = "/") { // 获取指定路径的资源ID const resourcesResponse = await apiGatewayClient.send(new GetResourcesCommand({ restApiId: apiId })); const targetResource = resourcesResponse.items.find(resource => resource.path === resourcePath); if (!targetResource) { throw new Error(`找不到路径为${resourcePath}的资源`); } const updateParams = { restApiId: apiId, resourceId: targetResource.id, httpMethod: httpMethod, // 比如GET、POST、PUT authorizationType: "COGNITO_USER_POOLS", authorizerId: authorizerId }; const command = new PutMethodCommand(updateParams); await apiGatewayClient.send(command); }
如果要添加新的自定义端点,需要先调用CreateResourceCommand创建资源,再调用PutMethodCommand添加方法,最后用PutIntegrationCommand配置后端集成(比如Lambda或HTTP服务),这里举个创建新资源的示例:
const { CreateResourceCommand, PutIntegrationCommand } = require("@aws-sdk/client-api-gateway"); async function createCustomEndpoint(apiId, parentResourceId, resourcePathPart, authorizerId) { // 创建新资源 const createResourceParams = { restApiId: apiId, parentId: parentResourceId, // 父资源ID,比如根资源ID pathPart: resourcePathPart // 比如"users",最终路径是"/users" }; const resourceResponse = await apiGatewayClient.send(new CreateResourceCommand(createResourceParams)); // 给资源添加POST方法并关联授权器 await attachAuthorizerToEndpoint(apiId, authorizerId, "POST", `/${resourcePathPart}`); // 配置后端集成(这里以Lambda为例) const integrationParams = { restApiId: apiId, resourceId: resourceResponse.id, httpMethod: "POST", type: "AWS_PROXY", integrationHttpMethod: "POST", uri: `arn:aws:apigateway:${process.env.AWS_REGION}:lambda:path/2015-03-31/functions/${process.env.TARGET_LAMBDA_ARN}/invocations` }; await apiGatewayClient.send(new PutIntegrationCommand(integrationParams)); return resourceResponse.id; }
最后必须部署API到某个阶段(比如dev、prod),否则API无法对外访问:
const { CreateDeploymentCommand } = require("@aws-sdk/client-api-gateway"); async function deployApi(apiId, stageName = "dev") { const deployParams = { restApiId: apiId, stageName: stageName, description: "Lambda自动部署的API" }; const command = new CreateDeploymentCommand(deployParams); await apiGatewayClient.send(command); }
把上面的函数整合到Lambda的入口handler里,完整示例:
const { APIGatewayClient, ImportRestApiCommand, GetResourcesCommand, CreateAuthorizerCommand, PutMethodCommand, CreateDeploymentCommand, CreateResourceCommand, PutIntegrationCommand } = require("@aws-sdk/client-api-gateway"); const apiGatewayClient = new APIGatewayClient({ region: process.env.AWS_REGION }); exports.handler = async (event) => { try { // 1. 导入Swagger创建API const apiId = await importApiFromSwagger(); console.log("创建的API ID:", apiId); // 2. 创建Cognito授权器 const authorizerId = await createCognitoAuthorizer(apiId); console.log("创建的授权器 ID:", authorizerId); // 3. 给根路径的GET方法关联授权器 await attachAuthorizerToEndpoint(apiId, authorizerId, "GET"); // 4. (可选)创建自定义端点 const rootResourceId = (await apiGatewayClient.send(new GetResourcesCommand({ restApiId: apiId }))).items.find(r => r.path === "/").id; await createCustomEndpoint(apiId, rootResourceId, "users", authorizerId); // 5. 部署API await deployApi(apiId); return { statusCode: 200, body: JSON.stringify({ message: "API创建并配置完成", apiUrl: `https://${apiId}.execute-api.${process.env.AWS_REGION}.amazonaws.com/dev` }) }; } catch (error) { console.error("操作失败:", error); return { statusCode: 500, body: JSON.stringify({ error: error.message }) }; } }; // 导入Swagger函数 async function importApiFromSwagger() { const swaggerDefinition = JSON.parse(process.env.SWAGGER_DEFINITION); const importParams = { body: swaggerDefinition, failOnWarnings: false }; const command = new ImportRestApiCommand(importParams); const response = await apiGatewayClient.send(command); return response.id; } // 创建Cognito授权器函数 async function createCognitoAuthorizer(apiId) { const createParams = { restApiId: apiId, name: "MyCognitoAuthorizer", type: "COGNITO_USER_POOLS", providerARNs: [ `arn:aws:cognito-idp:${process.env.AWS_REGION}:${process.env.AWS_ACCOUNT_ID}:userpool/${process.env.COGNITO_USER_POOL_ID}` ], identitySource: "method.request.header.Authorization", authorizerResultTtlInSeconds: 300 }; const command = new CreateAuthorizerCommand(createParams); const response = await apiGatewayClient.send(command); return response.id; } // 关联授权器到端点函数 async function attachAuthorizerToEndpoint(apiId, authorizerId, httpMethod, resourcePath = "/") { const resourcesResponse = await apiGatewayClient.send(new GetResourcesCommand({ restApiId: apiId })); const targetResource = resourcesResponse.items.find(resource => resource.path === resourcePath); if (!targetResource) throw new Error(`找不到路径为${resourcePath}的资源`); const updateParams = { restApiId: apiId, resourceId: targetResource.id, httpMethod: httpMethod, authorizationType: "COGNITO_USER_POOLS", authorizerId: authorizerId }; const command = new PutMethodCommand(updateParams); await apiGatewayClient.send(command); } // 创建自定义端点函数 async function createCustomEndpoint(apiId, parentResourceId, resourcePathPart, authorizerId) { const createResourceParams = { restApiId: apiId, parentId: parentResourceId, pathPart: resourcePathPart }; const resourceResponse = await apiGatewayClient.send(new CreateResourceCommand(createResourceParams)); await attachAuthorizerToEndpoint(apiId, authorizerId, "POST", `/${resourcePathPart}`); const integrationParams = { restApiId: apiId, resourceId: resourceResponse.id, httpMethod: "POST", type: "AWS_PROXY", integrationHttpMethod: "POST", uri: `arn:aws:apigateway:${process.env.AWS_REGION}:lambda:path/2015-03-31/functions/${process.env.TARGET_LAMBDA_ARN}/invocations` }; await apiGatewayClient.send(new PutIntegrationCommand(integrationParams)); return resourceResponse.id; } // 部署API函数 async function deployApi(apiId, stageName = "dev") { const deployParams = { restApiId: apiId, stageName: stageName, description: "Lambda自动部署的API" }; const command = new CreateDeploymentCommand(deployParams); await apiGatewayClient.send(command); }
- 如果你用Lambda的Node.js 18+运行环境,AWS SDK v3已经预装,但如果用到某些小众客户端,还是建议自己打包依赖或者用Lambda层。
- Swagger定义里可以预先配置授权信息,如果导入时已经指定了Cognito授权器的ARN,那导入后会自动关联,不需要手动调用
PutMethod。 - 测试时可以先在本地用AWS credentials运行代码,确认没问题再部署到Lambda。
内容的提问来源于stack exchange,提问作者Fayza Nawaz

