FastAPI
FastAPI é um framework para construção de APIs em Python.
O gerador FastAPI cria uma nova aplicação FastAPI com configuração de infraestrutura AWS CDK ou Terraform. O backend gerado utiliza AWS Lambda para implantação serverless, exposto via AWS API Gateway. Configura AWS Lambda Powertools para observabilidade, incluindo registro de logs, rastreamento com AWS X-Ray e métricas no CloudWatch.
Gerar uma API FastAPI
Seção intitulada “Gerar uma API FastAPI”Você pode gerar uma nova API FastAPI de duas formas:
- Instale o Nx Console VSCode Plugin se ainda não o fez
- Abra o console Nx no VSCode
- Clique em
Generate (UI)na seção "Common Nx Commands" - Procure por
@aws/nx-plugin - py#fast-api - Preencha os parâmetros obrigatórios
- Clique em
Generate
pnpm nx g @aws/nx-plugin:py#fast-apiyarn nx g @aws/nx-plugin:py#fast-apinpx nx g @aws/nx-plugin:py#fast-apibunx nx g @aws/nx-plugin:py#fast-apiVocê também pode realizar uma execução simulada para ver quais arquivos seriam alterados
pnpm nx g @aws/nx-plugin:py#fast-api --dry-runyarn nx g @aws/nx-plugin:py#fast-api --dry-runnpx nx g @aws/nx-plugin:py#fast-api --dry-runbunx nx g @aws/nx-plugin:py#fast-api --dry-run| Parâmetro | Tipo | Padrão | Descrição |
|---|---|---|---|
| name Obrigatório | string | - | Name of the API project to generate |
| computeType | string | ServerlessApiGatewayRestApi | The type of compute to use to deploy this API. Choose between ServerlessApiGatewayRestApi (default) or ServerlessApiGatewayHttpApi. |
| auth | string | IAM | The method used to authenticate with your API. Choose between IAM (default), Cognito or None. |
| directory | string | packages | The directory to store the application in. |
| iacProvider | string | Inherit | The preferred IaC provider. By default this is inherited from your initial selection. |
| moduleName | string | - | Python module name |
Saída do Gerador
Seção intitulada “Saída do Gerador”O gerador criará a seguinte estrutura de projeto no diretório <directory>/<api-name>:
- project.json Configuração do projeto e alvos de build
- pyproject.toml Configuração do projeto Python e dependências
Directory<module_name>
- __init__.py Inicialização do módulo
- init.py Configura o app FastAPI e middleware do powertools
- main.py Implementação da API
Directoryscripts
- generate_open_api.py Script para gerar schema OpenAPI a partir do app FastAPI
Infraestrutura
Seção intitulada “Infraestrutura”Como este gerador fornece infraestrutura como código com base no iacProvider escolhido, ele criará um projeto em packages/common que inclui os constructs CDK ou módulos Terraform relevantes.
O projeto comum de infraestrutura como código está estruturado da seguinte forma:
Directorypackages/common/constructs
Directorysrc
Directoryapp/ Constructs para infraestrutura específica de um projeto/gerador
- …
Directorycore/ Constructs genéricos reutilizados pelos constructs em
app- …
- index.ts Ponto de entrada exportando os constructs de
app
- project.json Metas de build e configuração do projeto
Directorypackages/common/terraform
Directorysrc
Directoryapp/ Módulos Terraform para infraestrutura específica de um projeto/gerador
- …
Directorycore/ Módulos genéricos reutilizados pelos módulos em
app- …
- project.json Metas de build e configuração do projeto
Para implantar sua API, os seguintes arquivos são gerados:
Directorypackages/common/constructs/src
Directoryapp
Directoryapis
- <project-name>.ts Construto CDK para implantar sua API
Directorycore
Directoryapi
- http-api.ts Construto CDK para implantar uma API HTTP (se você escolheu implantar uma API HTTP)
- rest-api.ts Construto CDK para implantar uma API REST (se você escolheu implantar uma API REST)
- utils.ts Utilitários para os construtos da API
Directorypackages/common/terraform/src
Directoryapp
Directoryapis
Directory<project-name>
- <project-name>.tf Módulo para implantar sua API
Directorycore
Directoryapi
Directoryhttp-api
- http-api.tf Módulo para implantar uma API HTTP (se você escolheu implantar uma API HTTP)
Directoryrest-api
- rest-api.tf Módulo para implantar uma API REST (se você escolheu implantar uma API REST)
Implementando sua API FastAPI
Seção intitulada “Implementando sua API FastAPI”A implementação principal da API está em main.py. É aqui que você define suas rotas e suas implementações. Exemplo:
from .init import app, tracerfrom pydantic import BaseModel
class Item(BaseModel): name: str
@app.get("/items/{item_id}")def get_item(item_id: int) -> Item: return Item(name=...)
@app.post("/items")def create_item(item: Item): return ...O gerador configura automaticamente vários recursos:
- Integração com AWS Lambda Powertools para observabilidade
- Middleware de tratamento de erros
- Correlação de requisições/respostas
- Coleta de métricas
- Handler AWS Lambda usando Mangum
Observabilidade com AWS Lambda Powertools
Seção intitulada “Observabilidade com AWS Lambda Powertools”O gerador configura logging estruturado usando AWS Lambda Powertools. Você pode acessar o logger nos handlers:
from .init import app, logger
@app.get("/items/{item_id}")def read_item(item_id: int): logger.info("Fetching item", extra={"item_id": item_id}) return {"item_id": item_id}O logger inclui automaticamente:
- IDs de correlação para rastreamento
- Caminho e método da requisição
- Informações de contexto do Lambda
- Indicadores de cold start
Rastreamento
Seção intitulada “Rastreamento”O rastreamento com AWS X-Ray é configurado automaticamente. Você pode adicionar subsegmentos personalizados:
from .init import app, tracer
@app.get("/items/{item_id}")@tracer.capture_methoddef read_item(item_id: int): with tracer.provider.in_subsegment("fetch-item-details"): return {"item_id": item_id}Métricas
Seção intitulada “Métricas”Métricas no CloudWatch são coletadas automaticamente. Você pode adicionar métricas personalizadas:
from .init import app, metricsfrom aws_lambda_powertools.metrics import MetricUnit
@app.get("/items/{item_id}")def read_item(item_id: int): metrics.add_metric(name="ItemViewed", unit=MetricUnit.Count, value=1) return {"item_id": item_id}Métricas padrão incluem:
- Contagem de requisições
- Contagem de sucessos/falhas
- Métricas de cold start
- Métricas por rota
Tratamento de Erros
Seção intitulada “Tratamento de Erros”O gerador inclui tratamento abrangente de erros:
from fastapi import HTTPException
@app.get("/items/{item_id}")def read_item(item_id: int): if item_id < 0: raise HTTPException(status_code=400, detail="Item ID must be positive") return {"item_id": item_id}Exceções não tratadas são capturadas pelo middleware e:
- Registram a exceção completa com stack trace
- Gravam métrica de falha
- Retornam resposta 500 segura ao cliente
- Preservam o ID de correlação
Recomenda-se especificar modelos de resposta para operações da API para melhor geração de código ao usar o api-connection. Veja detalhes aqui.
Streaming
Seção intitulada “Streaming”Com FastAPI, você pode transmitir respostas usando StreamingResponse.
Alterações na Infraestrutura
Seção intitulada “Alterações na Infraestrutura”Como o AWS API Gateway não suporta respostas em streaming, você precisará implantar sua API em uma plataforma que suporte isso. A opção mais simples é usar Lambda Function URL.
Para isso, substitua o construct gerado em common/constructs/src/app/apis/<name>-api.ts por um que implemente Function URL.
Exemplo de Construct com Function URL para Streaming
import { Duration, Stack, CfnOutput } from 'aws-cdk-lib';import { IGrantable, Grant } from 'aws-cdk-lib/aws-iam';import { Runtime, Code, Tracing, LayerVersion, FunctionUrlAuthType, InvokeMode, Function,} from 'aws-cdk-lib/aws-lambda';import { Construct } from 'constructs';import url from 'url';import { RuntimeConfig } from '../../core/runtime-config.js';
export class MyApi extends Construct { public readonly handler: Function;
constructor(scope: Construct, id: string) { super(scope, id);
this.handler = new Function(this, 'Handler', { runtime: Runtime.PYTHON_3_12, handler: 'run.sh', code: Code.fromAsset( url.fileURLToPath( new URL( '../../../../../../dist/packages/my_api/bundle-x86', import.meta.url, ), ), ), timeout: Duration.seconds(30), tracing: Tracing.ACTIVE, environment: { AWS_CONNECTION_REUSE_ENABLED: '1', }, });
const stack = Stack.of(this); this.handler.addLayers( LayerVersion.fromLayerVersionArn( this, 'LWALayer', `arn:aws:lambda:${stack.region}:753240598075:layer:LambdaAdapterLayerX86:24`, ), ); this.handler.addEnvironment('PORT', '8000'); this.handler.addEnvironment('AWS_LWA_INVOKE_MODE', 'response_stream'); this.handler.addEnvironment('AWS_LAMBDA_EXEC_WRAPPER', '/opt/bootstrap'); const functionUrl = this.handler.addFunctionUrl({ authType: FunctionUrlAuthType.AWS_IAM, invokeMode: InvokeMode.RESPONSE_STREAM, cors: { allowedOrigins: ['*'], allowedHeaders: [ 'authorization', 'content-type', 'x-amz-content-sha256', 'x-amz-date', 'x-amz-security-token', ], }, });
new CfnOutput(this, 'MyApiUrl', { value: functionUrl.url });
RuntimeConfig.ensure(this).config.apis = { ...RuntimeConfig.ensure(this).config.apis!, MyApi: functionUrl.url, }; }
public grantInvokeAccess(grantee: IGrantable) { Grant.addToPrincipal({ grantee, actions: ['lambda:InvokeFunctionUrl'], resourceArns: [this.handler.functionArn], conditions: { StringEquals: { 'lambda:FunctionUrlAuthType': 'AWS_IAM', }, }, }); }}Para configurar com Terraform, substitua a infraestrutura do API Gateway por Lambda Function URL.
Exemplo de Configuração Terraform para Function URL
data "aws_caller_identity" "current" {}data "aws_region" "current" {}
resource "aws_lambda_function" "my_api_handler" { filename = "../../../../../../dist/packages/my_api/bundle.zip" function_name = "my-api-handler" role = aws_iam_role.lambda_execution_role.arn handler = "run.sh" runtime = "python3.12" timeout = 30 source_code_hash = filebase64sha256("../../../../../../dist/packages/my_api/bundle.zip")
tracing_config { mode = "Active" }
environment { variables = { AWS_CONNECTION_REUSE_ENABLED = "1" PORT = "8000" AWS_LWA_INVOKE_MODE = "response_stream" AWS_LAMBDA_EXEC_WRAPPER = "/opt/bootstrap" } }
layers = [ "arn:aws:lambda:${data.aws_region.current.name}:753240598075:layer:LambdaAdapterLayerX86:24" ]
depends_on = [ aws_iam_role_policy_attachment.lambda_logs, aws_cloudwatch_log_group.lambda_logs, ]}
resource "aws_cloudwatch_log_group" "lambda_logs" { name = "/aws/lambda/my-api-handler" retention_in_days = 14}
resource "aws_iam_role" "lambda_execution_role" { name = "my-api-lambda-execution-role"
assume_role_policy = jsonencode({ Version = "2012-10-17" Statement = [ { Action = "sts:AssumeRole" Effect = "Allow" Principal = { Service = "lambda.amazonaws.com" } } ] })}
resource "aws_iam_role_policy_attachment" "lambda_logs" { role = aws_iam_role.lambda_execution_role.name policy_arn = "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"}
resource "aws_iam_role_policy_attachment" "lambda_xray" { role = aws_iam_role.lambda_execution_role.name policy_arn = "arn:aws:iam::aws:policy/AWSXRayDaemonWriteAccess"}
resource "aws_lambda_function_url" "my_api_url" { function_name = aws_lambda_function.my_api_handler.function_name authorization_type = "AWS_IAM" invoke_mode = "RESPONSE_STREAM"
cors { allow_origins = ["*"] allow_headers = [ "authorization", "content-type", "x-amz-content-sha256", "x-amz-date", "x-amz-security-token" ] }}
output "my_api_url" { value = aws_lambda_function_url.my_api_url.function_url}
resource "aws_ssm_parameter" "my_api_url" { name = "/runtime-config/apis/MyApi" type = "String" value = aws_lambda_function_url.my_api_url.function_url
tags = { Environment = "production" Service = "my-api" }}
resource "aws_iam_policy" "my_api_invoke_policy" { name = "my-api-invoke-policy"
policy = jsonencode({ Version = "2012-10-17" Statement = [ { Effect = "Allow" Action = "lambda:InvokeFunctionUrl" Resource = aws_lambda_function.my_api_handler.arn Condition = { StringEquals = { "lambda:FunctionUrlAuthType" = "AWS_IAM" } } } ] })}Implementação
Seção intitulada “Implementação”Após atualizar a infraestrutura, você pode implementar streaming no FastAPI. A API deve:
- Retornar
StreamingResponse - Declarar o tipo de retorno de cada chunk
- Adicionar a extensão OpenAPI
x-streaming: truese usar API Connection.
Exemplo de streaming de objetos JSON:
from pydantic import BaseModelfrom fastapi.responses import StreamingResponse
class Chunk(BaseModel): message: str timestamp: datetime
async def stream_chunks(): for i in range(0, 100): yield Chunk(message=f"This is chunk {i}", timestamp=datetime.now())
@app.get("/stream", openapi_extra={'x-streaming': True})def my_stream() -> Chunk: return StreamingResponse(stream_chunks(), media_type="application/json")Consumo
Seção intitulada “Consumo”Para consumir streams, utilize o Gerador de Conexão de API.
Implantando sua API FastAPI
Seção intitulada “Implantando sua API FastAPI”O gerador cria código de infraestrutura CDK/Terraform baseado no iacProvider. Use para implantar:
O construct CDK para implantação está em common/constructs. Use em uma aplicação CDK:
import { MyApi } from ':my-scope/common-constructs';
export class ExampleStack extends Stack { constructor(scope: Construct, id: string) { const api = new MyApi(this, 'MyApi', { integrations: MyApi.defaultIntegrations(this).build(), }); }}Isso configura:
- Função Lambda para cada operação
- API Gateway HTTP/REST
- Permissões IAM
- Log group no CloudWatch
- Configuração de X-Ray
- Namespace de métricas
Use os módulos Terraform em common/terraform:
module "my_api" { source = "../../common/terraform/src/app/apis/my-api"
env = { ENVIRONMENT = var.environment LOG_LEVEL = "INFO" }
additional_iam_policy_statements = [ # Permissões adicionais ]
tags = local.common_tags}Isso configura:
- Função Lambda para todas as rotas
- API Gateway HTTP/REST
- Permissões IAM
- Log group no CloudWatch
- Configuração de CORS
Saídas do módulo:
output "api_url" { value = module.my_api.stage_invoke_url}
output "lambda_function_name" { value = module.my_api.lambda_function_name}
output "lambda_execution_role_arn" { value = module.my_api.lambda_execution_role_arn}Personalize CORS:
module "my_api" { source = "../../common/terraform/src/app/apis/my-api"
cors_allow_origins = ["https://myapp.com"] cors_allow_methods = ["GET", "POST"] cors_allow_headers = ["authorization"]}Se selecionar None para auth, Checkov pode reportar falhas:
Check: CKV_AWS_309: "Ensure API GatewayV2 routes specify an authorization type" FAILED for resource: aws_apigatewayv2_route.proxy_routes["PUT"]Check: CKV_AWS_59: "Ensure there is no open access to back-end resources through API" FAILED for resource: aws_api_gateway_method.proxy_methodAdicione comentários de supressão se necessário.
Integrações
Seção intitulada “Integrações”Os construtos CDK da API REST/HTTP são configurados para fornecer uma interface type-safe para definir integrações para cada uma de suas operações.
Os construtos CDK fornecem suporte completo a integrações type-safe conforme descrito abaixo.
Integrações Padrão
Seção intitulada “Integrações Padrão”Você pode usar o método estático defaultIntegrations para utilizar o padrão padrão, que define uma função AWS Lambda individual para cada operação:
new MyApi(this, 'MyApi', { integrations: MyApi.defaultIntegrations(this).build(),});Os módulos Terraform automaticamente usam o padrão de roteador com uma única função Lambda. Nenhuma configuração adicional é necessária:
module "my_api" { source = "../../common/terraform/src/app/apis/my-api"
# O módulo cria automaticamente uma única função Lambda # que processa todas as operações da API tags = local.common_tags}Acessando Integrações
Seção intitulada “Acessando Integrações”Você pode acessar as funções AWS Lambda subjacentes através da propriedade integrations do construto da API, de forma type-safe. Por exemplo, se sua API define uma operação chamada sayHello e você precisa adicionar permissões a esta função, você pode fazer isso da seguinte forma:
const api = new MyApi(this, 'MyApi', { integrations: MyApi.defaultIntegrations(this).build(),});
// sayHello é tipado conforme as operações definidas em sua APIapi.integrations.sayHello.handler.addToRolePolicy(new PolicyStatement({ effect: Effect.ALLOW, actions: [...], resources: [...],}));Com o padrão de roteador do Terraform, há apenas uma função Lambda. Você pode acessá-la através dos outputs do módulo:
# Conceder permissões adicionais à única função Lambdaresource "aws_iam_role_policy" "additional_permissions" { name = "additional-api-permissions" role = module.my_api.lambda_execution_role_name
policy = jsonencode({ Version = "2012-10-17" Statement = [ { Effect = "Allow" Action = [ "s3:GetObject", "s3:PutObject" ] Resource = "arn:aws:s3:::my-bucket/*" } ] })}Personalizando Opções Padrão
Seção intitulada “Personalizando Opções Padrão”Se você deseja personalizar as opções usadas ao criar a função Lambda para cada integração padrão, pode usar o método withDefaultOptions. Por exemplo, se deseja que todas suas funções Lambda residam em uma VPC:
const vpc = new Vpc(this, 'Vpc', ...);
new MyApi(this, 'MyApi', { integrations: MyApi.defaultIntegrations(this) .withDefaultOptions({ vpc, }) .build(),});Para personalizar opções como configuração de VPC, você precisa editar o módulo Terraform gerado. Por exemplo, para adicionar suporte a VPC em todas as funções Lambda:
# Adicionar variáveis de VPCvariable "vpc_subnet_ids" { description = "Lista de IDs de subnets VPC para a função Lambda" type = list(string) default = []}
variable "vpc_security_group_ids" { description = "Lista de IDs de grupos de segurança VPC para a função Lambda" type = list(string) default = []}
# Atualizar o recurso da função Lambdaresource "aws_lambda_function" "api_lambda" { # ... configuração existente ...
# Adicionar configuração de VPC vpc_config { subnet_ids = var.vpc_subnet_ids security_group_ids = var.vpc_security_group_ids }}Então usar o módulo com configuração de VPC:
module "my_api" { source = "../../common/terraform/src/app/apis/my-api"
# Configuração de VPC vpc_subnet_ids = [aws_subnet.private_a.id, aws_subnet.private_b.id] vpc_security_group_ids = [aws_security_group.lambda_sg.id]
tags = local.common_tags}Sobrescrevendo Integrações
Seção intitulada “Sobrescrevendo Integrações”Você também pode sobrescrever integrações para operações específicas usando o método withOverrides. Cada sobrescrita deve especificar uma propriedade integration que é tipada ao construto de integração CDK apropriado para a API HTTP ou REST. O método withOverrides também é type-safe. Por exemplo, se você deseja sobrescrever uma API getDocumentation para apontar para documentação hospedada em um site externo:
new MyApi(this, 'MyApi', { integrations: MyApi.defaultIntegrations(this) .withOverrides({ getDocumentation: { integration: new HttpIntegration('https://example.com/documentation'), }, }) .build(),});Você notará que a integração sobrescrita não terá mais uma propriedade handler quando acessada via api.integrations.getDocumentation.
Você pode adicionar propriedades adicionais a uma integração que também serão tipadas adequadamente, permitindo que outros tipos de integração sejam abstraídos mas permaneçam type-safe. Por exemplo, se você criou uma integração S3 para uma API REST e depois deseja referenciar o bucket para uma operação específica:
const storageBucket = new Bucket(this, 'Bucket', { ... });
const apiGatewayRole = new Role(this, 'ApiGatewayS3Role', { assumedBy: new ServicePrincipal('apigateway.amazonaws.com'),});
storageBucket.grantRead(apiGatewayRole);
const api = new MyApi(this, 'MyApi', { integrations: MyApi.defaultIntegrations(this) .withOverrides({ getFile: { bucket: storageBucket, integration: new AwsIntegration({ service: 's3', integrationHttpMethod: 'GET', path: `${storageBucket.bucketName}/{fileName}`, options: { credentialsRole: apiGatewayRole, requestParameters: { 'integration.request.path.fileName': 'method.request.querystring.fileName', }, integrationResponses: [{ statusCode: '200' }], }, }), options: { requestParameters: { 'method.request.querystring.fileName': true, }, methodResponses: [{ statusCode: '200', }], } }, }) .build(),});
// Posteriormente, talvez em outro arquivo, você pode acessar a propriedade bucket que definimos// de forma type-safeapi.integrations.getFile.bucket.grantRead(...);Sobrescrevendo Autorizadores
Seção intitulada “Sobrescrevendo Autorizadores”Você também pode fornecer options em sua integração para sobrescrever opções específicas de método como autorizadores. Por exemplo, se desejar usar autenticação Cognito para sua operação getDocumentation:
new MyApi(this, 'MyApi', { integrations: MyApi.defaultIntegrations(this) .withOverrides({ getDocumentation: { integration: new HttpIntegration('https://example.com/documentation'), options: { authorizer: new CognitoUserPoolsAuthorizer(...) // para REST, ou HttpUserPoolAuthorizer para HTTP API } }, }) .build(),});Integrações Explícitas
Seção intitulada “Integrações Explícitas”Se preferir, você pode optar por não usar as integrações padrão e fornecer diretamente uma para cada operação. Isso é útil se, por exemplo, cada operação precisar usar um tipo diferente de integração ou se você quiser receber um erro de tipo ao adicionar novas operações:
new MyApi(this, 'MyApi', { integrations: { sayHello: { integration: new LambdaIntegration(...), }, getDocumentation: { integration: new HttpIntegration(...), }, },});Para integrações explícitas por operação com Terraform, você deve modificar o módulo específico da aplicação gerado para substituir a integração proxy padrão por integrações específicas para cada operação.
Edite packages/common/terraform/src/app/apis/my-api/my-api.tf:
- Remover as rotas proxy padrão (ex:
resource "aws_apigatewayv2_route" "proxy_routes") - Substituir a função Lambda única por funções individuais para cada operação
- Criar integrações e rotas específicas para cada operação, reutilizando o mesmo pacote ZIP:
# Remover função Lambda única padrão resource "aws_lambda_function" "api_lambda" { filename = data.archive_file.lambda_zip.output_path function_name = "MyApiHandler" role = aws_iam_role.lambda_execution_role.arn handler = "index.handler" runtime = "nodejs22.x" timeout = 30 # ... restante da configuração }
# Remover integração proxy padrão resource "aws_apigatewayv2_integration" "lambda_integration" { api_id = module.http_api.api_id integration_type = "AWS_PROXY" integration_uri = aws_lambda_function.api_lambda.invoke_arn # ... restante da configuração }
# Remover rotas proxy padrão resource "aws_apigatewayv2_route" "proxy_routes" { for_each = toset(["GET", "POST", "PUT", "PATCH", "DELETE", "HEAD"]) api_id = module.http_api.api_id route_key = "${each.key} /{proxy+}" target = "integrations/${aws_apigatewayv2_integration.lambda_integration.id}" # ... restante da configuração }
# Adicionar funções Lambda individuais para cada operação usando o mesmo pacote resource "aws_lambda_function" "say_hello_handler" { filename = data.archive_file.lambda_zip.output_path function_name = "MyApi-SayHello" role = aws_iam_role.lambda_execution_role.arn handler = "sayHello.handler" # Handler específico para esta operação runtime = "nodejs22.x" timeout = 30 source_code_hash = data.archive_file.lambda_zip.output_base64sha256
tracing_config { mode = "Active" }
environment { variables = merge({ AWS_CONNECTION_REUSE_ENABLED = "1" }, var.env) }
tags = var.tags }
resource "aws_lambda_function" "get_documentation_handler" { filename = data.archive_file.lambda_zip.output_path function_name = "MyApi-GetDocumentation" role = aws_iam_role.lambda_execution_role.arn handler = "getDocumentation.handler" # Handler específico para esta operação runtime = "nodejs22.x" timeout = 30 source_code_hash = data.archive_file.lambda_zip.output_base64sha256
tracing_config { mode = "Active" }
environment { variables = merge({ AWS_CONNECTION_REUSE_ENABLED = "1" }, var.env) }
tags = var.tags }
# Adicionar integrações específicas para cada operação resource "aws_apigatewayv2_integration" "say_hello_integration" { api_id = module.http_api.api_id integration_type = "AWS_PROXY" integration_uri = aws_lambda_function.say_hello_handler.invoke_arn payload_format_version = "2.0" timeout_milliseconds = 30000 }
resource "aws_apigatewayv2_integration" "get_documentation_integration" { api_id = module.http_api.api_id integration_type = "HTTP_PROXY" integration_uri = "https://example.com/documentation" integration_method = "GET" }
# Adicionar rotas específicas para cada operação resource "aws_apigatewayv2_route" "say_hello_route" { api_id = module.http_api.api_id route_key = "POST /sayHello" target = "integrations/${aws_apigatewayv2_integration.say_hello_integration.id}" authorization_type = "AWS_IAM" }
resource "aws_apigatewayv2_route" "get_documentation_route" { api_id = module.http_api.api_id route_key = "GET /documentation" target = "integrations/${aws_apigatewayv2_integration.get_documentation_integration.id}" authorization_type = "NONE" }
# Adicionar permissões Lambda para cada função resource "aws_lambda_permission" "say_hello_permission" { statement_id = "AllowExecutionFromAPIGateway-SayHello" action = "lambda:InvokeFunction" function_name = aws_lambda_function.say_hello_handler.function_name principal = "apigateway.amazonaws.com" source_arn = "${module.http_api.api_execution_arn}/*/*" }
resource "aws_lambda_permission" "get_documentation_permission" { statement_id = "AllowExecutionFromAPIGateway-GetDocumentation" action = "lambda:InvokeFunction" function_name = aws_lambda_function.get_documentation_handler.function_name principal = "apigateway.amazonaws.com" source_arn = "${module.http_api.api_execution_arn}/*/*" }# Remover função Lambda única padrão resource "aws_lambda_function" "api_lambda" { filename = data.archive_file.lambda_zip.output_path function_name = "MyApiHandler" role = aws_iam_role.lambda_execution_role.arn handler = "index.handler" runtime = "nodejs22.x" timeout = 30 # ... restante da configuração }
# Remover integração proxy padrão resource "aws_apigatewayv2_integration" "lambda_integration" { api_id = module.http_api.api_id integration_type = "AWS_PROXY" integration_uri = aws_lambda_function.api_lambda.invoke_arn # ... restante da configuração }
# Remover rotas proxy padrão resource "aws_apigatewayv2_route" "proxy_routes" { for_each = toset(["GET", "POST", "PUT", "PATCH", "DELETE", "HEAD"]) api_id = module.http_api.api_id route_key = "${each.key} /{proxy+}" target = "integrations/${aws_apigatewayv2_integration.lambda_integration.id}" # ... restante da configuração }
# Adicionar funções Lambda individuais para cada operação usando o mesmo pacote resource "aws_lambda_function" "say_hello_handler" { filename = data.archive_file.lambda_zip.output_path function_name = "MyApi-SayHello" role = aws_iam_role.lambda_execution_role.arn handler = "sayHello.handler" # Handler específico para esta operação runtime = "nodejs22.x" timeout = 30 source_code_hash = data.archive_file.lambda_zip.output_base64sha256
tracing_config { mode = "Active" }
environment { variables = merge({ AWS_CONNECTION_REUSE_ENABLED = "1" }, var.env) }
tags = var.tags }
resource "aws_lambda_function" "get_documentation_handler" { filename = data.archive_file.lambda_zip.output_path function_name = "MyApi-GetDocumentation" role = aws_iam_role.lambda_execution_role.arn handler = "getDocumentation.handler" # Handler específico para esta operação runtime = "nodejs22.x" timeout = 30 source_code_hash = data.archive_file.lambda_zip.output_base64sha256
tracing_config { mode = "Active" }
environment { variables = merge({ AWS_CONNECTION_REUSE_ENABLED = "1" }, var.env) }
tags = var.tags }
# Adicionar recursos e métodos específicos para cada operação resource "aws_api_gateway_resource" "say_hello_resource" { rest_api_id = module.rest_api.api_id parent_id = module.rest_api.api_root_resource_id path_part = "sayHello" }
resource "aws_api_gateway_method" "say_hello_method" { rest_api_id = module.rest_api.api_id resource_id = aws_api_gateway_resource.say_hello_resource.id http_method = "POST" authorization = "AWS_IAM" }
resource "aws_api_gateway_integration" "say_hello_integration" { rest_api_id = module.rest_api.api_id resource_id = aws_api_gateway_resource.say_hello_resource.id http_method = aws_api_gateway_method.say_hello_method.http_method
integration_http_method = "POST" type = "AWS_PROXY" uri = aws_lambda_function.say_hello_handler.invoke_arn }
resource "aws_api_gateway_resource" "get_documentation_resource" { rest_api_id = module.rest_api.api_id parent_id = module.rest_api.api_root_resource_id path_part = "documentation" }
resource "aws_api_gateway_method" "get_documentation_method" { rest_api_id = module.rest_api.api_id resource_id = aws_api_gateway_resource.get_documentation_resource.id http_method = "GET" authorization = "NONE" }
resource "aws_api_gateway_integration" "get_documentation_integration" { rest_api_id = module.rest_api.api_id resource_id = aws_api_gateway_resource.get_documentation_resource.id http_method = aws_api_gateway_method.get_documentation_method.http_method
integration_http_method = "GET" type = "HTTP" uri = "https://example.com/documentation" }
# Atualizar deployment para depender das novas integrações~ resource "aws_api_gateway_deployment" "api_deployment" { rest_api_id = module.rest_api.api_id
depends_on = [ aws_api_gateway_integration.lambda_integration, aws_api_gateway_integration.say_hello_integration, aws_api_gateway_integration.get_documentation_integration, ]
lifecycle { create_before_destroy = true }
triggers = { redeployment = sha1(jsonencode([ aws_api_gateway_integration.say_hello_integration, aws_api_gateway_integration.get_documentation_integration, ])) } }
# Adicionar permissões Lambda para cada função resource "aws_lambda_permission" "say_hello_permission" { statement_id = "AllowExecutionFromAPIGateway-SayHello" action = "lambda:InvokeFunction" function_name = aws_lambda_function.say_hello_handler.function_name principal = "apigateway.amazonaws.com" source_arn = "${module.rest_api.api_execution_arn}/*/*" }
resource "aws_lambda_permission" "get_documentation_permission" { statement_id = "AllowExecutionFromAPIGateway-GetDocumentation" action = "lambda:InvokeFunction" function_name = aws_lambda_function.get_documentation_handler.function_name principal = "apigateway.amazonaws.com" source_arn = "${module.rest_api.api_execution_arn}/*/*" }Padrão de Roteador
Seção intitulada “Padrão de Roteador”Se você preferir implantar uma única função Lambda para atender todas as requisições da API, pode livremente editar o método defaultIntegrations de sua API para criar uma única função em vez de uma por integração:
export class MyApi<...> extends ... {
public static defaultIntegrations = (scope: Construct) => { const router = new Function(scope, 'RouterHandler', { ... }); return IntegrationBuilder.rest({ ... defaultIntegrationOptions: {}, buildDefaultIntegration: (op) => { return { // Referencia o mesmo router lambda handler em todas as integrações integration: new LambdaIntegration(router), }; }, }); };}Você pode modificar o código de outras formas se preferir, por exemplo pode definir a função router como parâmetro para defaultIntegrations em vez de construí-la dentro do método.
Os módulos Terraform automaticamente usam o padrão de roteador - esta é a abordagem padrão e única suportada. O módulo gerado cria uma única função Lambda que processa todas as operações da API.
Você pode simplesmente instanciar o módulo padrão para obter o padrão de roteador:
# Padrão de roteador - função Lambda única para todas as operaçõesmodule "my_api" { source = "../../common/terraform/src/app/apis/my-api"
# Função Lambda única processa todas as operações automaticamente tags = local.common_tags}Geração de Código
Seção intitulada “Geração de Código”Como as operações são definidas em Python e a infraestrutura em TypeScript, geramos código para fornecer metadados ao construct CDK.
Um alvo generate:<ApiName>-metadata é adicionado ao project.json para gerar arquivos como packages/common/constructs/src/generated/my-api/metadata.gen.ts.
Use nx watch para regenerar tipos automaticamente:
pnpm nx watch --projects=<FastAPIProject> -- \pnpm nx run <InfraProject>:"generate:<ApiName>-metadata"yarn nx watch --projects=<FastAPIProject> -- \yarn nx run <InfraProject>:"generate:<ApiName>-metadata"npx nx watch --projects=<FastAPIProject> -- \npx nx run <InfraProject>:"generate:<ApiName>-metadata"bunx nx watch --projects=<FastAPIProject> -- \bunx nx run <InfraProject>:"generate:<ApiName>-metadata"Concedendo Acesso (Somente IAM)
Seção intitulada “Concedendo Acesso (Somente IAM)”Para autenticação IAM, use grantInvokeAccess:
api.grantInvokeAccess(myIdentityPool.authenticatedRole);resource "aws_iam_policy" "api_invoke_policy" { name = "MyApiInvokePolicy" policy = jsonencode({ Statement = [ { Action = "execute-api:Invoke" Resource = "${module.my_api.api_execution_arn}/*/*" } ] })}
resource "aws_iam_role_policy_attachment" "api_invoke_access" { role = aws_iam_role.authenticated_user_role.name policy_arn = aws_iam_policy.api_invoke_policy.arn}Principais saídas:
module.my_api.api_execution_arnmodule.my_api.api_arnmodule.my_api.lambda_function_arn
Desenvolvimento Local
Seção intitulada “Desenvolvimento Local”Execute o servidor local com:
pnpm nx run my-api:serveyarn nx run my-api:servenpx nx run my-api:servebunx nx run my-api:serveIsso inicia um servidor FastAPI com:
- Recarregamento automático
- Documentação interativa em
/docsou/redoc - Schema OpenAPI em
/openapi.json
Invocando sua API FastAPI
Seção intitulada “Invocando sua API FastAPI”Para consumir a API de um site React, use o api-connection.