FastAPI
FastAPI é um framework para construir APIs em Python.
O gerador FastAPI cria um novo FastAPI com configuração de infraestrutura AWS CDK ou Terraform. O backend gerado usa AWS Lambda para implantação serverless, exposto via uma API do AWS API Gateway. Ele configura AWS Lambda Powertools para observabilidade, incluindo logging, rastreamento AWS X-Ray e Métricas do Cloudwatch.
Gerar um FastAPI
Seção intitulada “Gerar um FastAPI”Você pode gerar um novo FastAPI de duas maneiras:
Execute este gerador@aws/nx-plugin:py#api
pnpm nx g @aws/nx-plugin:py#api yarn nx g @aws/nx-plugin:py#api npx nx g @aws/nx-plugin:py#api bunx nx g @aws/nx-plugin:py#api- 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#api - Preencha os parâmetros obrigatórios
- Clique em
Generate
Monte seu comando10
Obrigatório
nameObrigatóriostringNome do projeto de API a ser gerado
frameworkenumPadrão:fastapiO framework de API a ser utilizado.
fastapiintegrationPatternenumPadrão:isolatedComo as integrações do API Gateway são geradas para a API. Escolha entre isolated (padrão) e shared.
isolatedsharedauthenumPadrão:iamO método usado para autenticar com sua API. Escolha entre iam (padrão), cognito ou custom.
iamcognitocustomdirectorystringPadrão:packagesO diretório para armazenar a aplicação.
iacenumPadrão:inheritO provedor IaC preferido. Por padrão, isso é herdado da sua seleção inicial.
inheritcdkterraforminfraenumPadrão:rest-lambdaO tipo de infraestrutura a ser usado para implantar esta API.
rest-lambdahttp-lambdanonesubDirectorystringO subdiretório onde o projeto é colocado. Por padrão, este é o nome do projeto.
moduleNamestringNome do módulo Python
preferInstallDependenciesbooleanPadrão:trueSe deve preferir instalar dependências após a execução do gerador. Defina como false para adiar a instalação ao executar múltiplos geradores em lote (uma instalação ainda é executada se necessário para que geradores subsequentes possam calcular o grafo de projetos Nx); instale uma vez no final.
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 e dependências do projeto Python
- run.sh Script de bootstrap do Lambda Web Adapter para iniciar o app FastAPI via uvicorn
Directory<module_name>
- __init__.py Inicialização do módulo
- init.py Configura o app FastAPI e configura o middleware powertools
- main.py Implementação da API
Directoryscripts
- generate_open_api.py Script para gerar um esquema OpenAPI a partir do app FastAPI
Infraestrutura
Seção intitulada “Infraestrutura”Como este gerador fornece infraestrutura como código baseada no seu iac escolhido, ele criará um projeto em packages/common que inclui os constructs CDK relevantes ou módulos Terraform.
O projeto comum de infraestrutura como código é estruturado da seguinte forma:
Directorypackages/common/constructs
Directorysrc
Directoryapp/ Constructs for infrastructure specific to a project/generator
- …
Directorycore/ Generic constructs which are reused by constructs in
app- …
- index.ts Entry point exporting constructs from
app
- project.json Project build targets and configuration
Directorypackages/common/terraform
Directorysrc
Directoryapp/ Terraform modules for infrastructure specific to a project/generator
- …
Directorycore/ Generic modules which are reused by modules in
app- …
- project.json Project build targets and configuration
Para implantar sua API, os seguintes arquivos são gerados:
Directorypackages/common/constructs/src
Directoryapp
Directoryapis
- <project-name>.ts CDK construct for deploying your API
Directorycore
Directoryapi
- http-api.ts CDK construct for deploying an HTTP API (if you selected to deploy an HTTP API)
- rest-api.ts CDK construct for deploying a REST API (if you selected to deploy a REST API)
- utils.ts Utilities for the API constructs
Directorypackages/common/terraform/src
Directoryapp
Directoryapis
Directory<project-name>
- <project-name>.tf Module for deploying your API
Directorycore
Directoryapi
Directoryhttp-api
- http-api.tf Module for deploying an HTTP API (if you selected to deploy an HTTP API)
Directoryrest-api
- rest-api.tf Module for deploying a REST API (if you selected to deploy a REST API)
Arquitetura
Seção intitulada “Arquitetura”A aplicação implantada possui a seguinte arquitetura: uma API do API Gateway na frente de uma função Lambda executando seu manipulador.
As REST APIs incluem um Web ACL do AWS WAFv2 na frente do estágio do API Gateway com o conjunto de regras padrão gerenciado pela AWS habilitado.
As HTTP APIs não suportam WAF diretamente — se você precisar de proteção WAF, escolha REST API em vez disso ou coloque a HTTP API atrás de uma distribuição CloudFront.
Implementando seu FastAPI
Seção intitulada “Implementando seu FastAPI”A implementação principal da API está em main.py. É aqui que você define suas rotas de API e suas implementações. Aqui está um exemplo:
from pydantic import BaseModelfrom .init import app, tracer
class Item(BaseModel): name: str
@app.get("/items/{item_id}")@tracer.capture_methoddef get_item(item_id: int) -> Item: return Item(name=f"Item {item_id}")
@app.post("/items")@tracer.capture_methoddef create_item(item: Item) -> Item: return itemO gerador configura vários recursos automaticamente:
- Integração do AWS Lambda Powertools para observabilidade
- Middleware de tratamento de erros
- Correlação de solicitação/resposta
- Coleta de métricas
- Implantação do AWS Lambda via Lambda Web Adapter com uvicorn
- Streaming type-safe (somente API REST)
Observabilidade com AWS Lambda Powertools
Seção intitulada “Observabilidade com AWS Lambda Powertools”Logging
Seção intitulada “Logging”O gerador configura logging estruturado usando AWS Lambda Powertools. Você pode acessar o logger em seus manipuladores de rota:
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 de solicitações
- Caminho da solicitação, rota correspondente e método
Rastreamento
Seção intitulada “Rastreamento”O rastreamento AWS X-Ray é configurado automaticamente. Você pode adicionar subsegmentos personalizados aos seus rastreamentos:
from .init import app, tracer
@app.get("/items/{item_id}")@tracer.capture_methoddef read_item(item_id: int): # Creates a new subsegment with tracer.provider.in_subsegment("fetch-item-details"): # Your logic here return {"item_id": item_id}Métricas
Seção intitulada “Métricas”As métricas do CloudWatch são coletadas automaticamente para cada solicitação. 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}As métricas padrão incluem:
- Contagens de solicitações
- Contagens de sucesso/falha
- Métricas por rota (via uma dimensão
routede<method> <path>)
Tratamento de Erros
Seção intitulada “Tratamento de Erros”O gerador inclui tratamento de erros abrangente:
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
- Registram uma métrica de falha
- Retornam uma resposta 500 segura ao cliente
- Preservam o ID de correlação
Acessando o Usuário Chamador
Seção intitulada “Acessando o Usuário Chamador”Quando sua API é protegida por autenticação, seus manipuladores de rota frequentemente precisam saber quem está chamando. O FastAPI gerado é executado dentro do AWS Lambda via o Lambda Web Adapter, que encaminha o contexto de solicitação do API Gateway como JSON no cabeçalho x-amzn-request-context. Você pode lê-lo do Request do FastAPI para extrair a identidade do chamador.
Como exemplo, vamos adicionar um endpoint /me que retorna detalhes sobre o usuário chamador. Implementaremos a extração como uma dependência do FastAPI para que possa ser reutilizada entre rotas. A forma do contexto de solicitação — e, portanto, como você extrai a identidade — depende tanto do seu método auth selecionado quanto se você implantou uma API REST ou HTTP.
Para autenticação IAM, procuramos o chamador no Cognito usando o sub extraído do contexto de solicitação do API Gateway. Crie identity.py ao lado de main.py:
import jsonimport osfrom typing import Annotated
from boto3 import clientfrom fastapi import Depends, HTTPException, Requestfrom pydantic import BaseModel
cognito = client("cognito-idp")
class Identity(BaseModel): sub: str username: str
def get_identity(request: Request) -> Identity: # The Lambda Web Adapter forwards the API Gateway request context as JSON request_context_header = request.headers.get("x-amzn-request-context") if not request_context_header: raise HTTPException(status_code=403, detail="Unable to determine calling user")
request_context = json.loads(request_context_header) provider = request_context.get("identity", {}).get("cognitoAuthenticationProvider")
sub = provider.split(":")[-1] if provider else None if not sub: raise HTTPException(status_code=403, detail="Unable to determine calling user")
users = cognito.list_users( # Assumes user pool id is configured in lambda environment UserPoolId=os.environ["USER_POOL_ID"], Limit=1, Filter=f'sub="{sub}"', ).get("Users", [])
if len(users) != 1: raise HTTPException(status_code=403, detail=f"No user found with subjectId {sub}")
return Identity(sub=sub, username=users[0]["Username"])
CurrentUser = Annotated[Identity, Depends(get_identity)]import jsonimport osfrom typing import Annotated
from boto3 import clientfrom fastapi import Depends, HTTPException, Requestfrom pydantic import BaseModel
cognito = client("cognito-idp")
class Identity(BaseModel): sub: str username: str
def get_identity(request: Request) -> Identity: # The Lambda Web Adapter forwards the API Gateway request context as JSON request_context_header = request.headers.get("x-amzn-request-context") if not request_context_header: raise HTTPException(status_code=403, detail="Unable to determine calling user")
request_context = json.loads(request_context_header) amr = ( request_context.get("authorizer", {}) .get("iam", {}) .get("cognitoIdentity", {}) .get("amr", []) ) sign_in = next((s for s in amr if ":CognitoSignIn:" in s), None) sub = sign_in.split(":")[-1] if sign_in else None
if not sub: raise HTTPException(status_code=403, detail="Unable to determine calling user")
users = cognito.list_users( # Assumes user pool id is configured in lambda environment UserPoolId=os.environ["USER_POOL_ID"], Limit=1, Filter=f'sub="{sub}"', ).get("Users", [])
if len(users) != 1: raise HTTPException(status_code=403, detail=f"No user found with subjectId {sub}")
return Identity(sub=sub, username=users[0]["Username"])
CurrentUser = Annotated[Identity, Depends(get_identity)]Com auth: 'cognito', o autorizador Cognito User Pools do API Gateway verifica o JWT que o chamador fornece no cabeçalho Authorization e coloca as claims verificadas no contexto de solicitação.
Crie identity.py ao lado de main.py:
import jsonfrom typing import Annotated
from fastapi import Depends, HTTPException, Requestfrom pydantic import BaseModel
class Identity(BaseModel): sub: str username: str
def get_identity(request: Request) -> Identity: # The Lambda Web Adapter forwards the API Gateway request context as JSON request_context_header = request.headers.get("x-amzn-request-context") if not request_context_header: raise HTTPException(status_code=403, detail="Unable to determine calling user")
request_context = json.loads(request_context_header) claims = request_context.get("authorizer", {}).get("claims", {})
sub = claims.get("sub") username = claims.get("username")
if not sub or not username: raise HTTPException(status_code=403, detail="Unable to determine calling user")
return Identity(sub=sub, username=username)
CurrentUser = Annotated[Identity, Depends(get_identity)]APIs HTTP usam um autorizador JWT que coloca as claims verificadas sob authorizer.jwt.claims:
import jsonfrom typing import Annotated
from fastapi import Depends, HTTPException, Requestfrom pydantic import BaseModel
class Identity(BaseModel): sub: str username: str
def get_identity(request: Request) -> Identity: # The Lambda Web Adapter forwards the API Gateway request context as JSON request_context_header = request.headers.get("x-amzn-request-context") if not request_context_header: raise HTTPException(status_code=403, detail="Unable to determine calling user")
request_context = json.loads(request_context_header) claims = request_context.get("authorizer", {}).get("jwt", {}).get("claims", {})
sub = claims.get("sub") username = claims.get("username")
if not sub or not username: raise HTTPException(status_code=403, detail="Unable to determine calling user")
return Identity(sub=sub, username=username)
CurrentUser = Annotated[Identity, Depends(get_identity)]Você pode então injetar a dependência CurrentUser em qualquer rota que precise da identidade do chamador:
from .identity import CurrentUser, Identityfrom .init import app, tracer
@app.get("/me")@tracer.capture_methoddef me(identity: CurrentUser) -> Identity: return identityStreaming
Seção intitulada “Streaming”O FastAPI gerado suporta respostas de streaming prontas para uso ao usar uma API REST. A infraestrutura é configurada para usar o AWS Lambda Web Adapter para executar seu FastAPI via uvicorn dentro do Lambda, com ResponseTransferMode.STREAM no API Gateway para todas as operações da API REST, o que permite que o streaming funcione ao lado de operações sem streaming.
Usando JsonStreamingResponse
Seção intitulada “Usando JsonStreamingResponse”O init.py gerado exporta uma classe JsonStreamingResponse que fornece streaming type-safe com geração adequada de esquema OpenAPI. Isso garante que o gerador connection possa produzir métodos de cliente de streaming corretamente tipados.
from pydantic import BaseModelfrom .init import app, JsonStreamingResponse
class Chunk(BaseModel): message: str
async def generate_chunks(): for i in range(100): yield Chunk(message=f"This is chunk {i}")
@app.post( "/stream", response_class=JsonStreamingResponse, responses={200: JsonStreamingResponse.openapi_response(Chunk, "Stream of chunks")},)async def my_stream() -> JsonStreamingResponse: return JsonStreamingResponse(generate_chunks())A classe JsonStreamingResponse:
- Serializa modelos Pydantic para o formato JSON Lines (
application/jsonl) - Fornece um auxiliar
openapi_responseque gera o esquema OpenAPI correto comitemSchema, permitindo que o geradorconnectionproduza métodos de cliente de streaming type-safe
Consumo
Seção intitulada “Consumo”Para consumir um fluxo de respostas, você pode fazer uso do gerador connection que fornecerá um método type-safe para iterar sobre seus chunks transmitidos.
Implantando seu FastAPI
Seção intitulada “Implantando seu FastAPI”O gerador FastAPI cria infraestrutura como código CDK ou Terraform com base no seu iac selecionado. Você pode usar isso para implantar seu FastAPI.
O construto CDK para implantar sua API na pasta common/constructs. Você pode usar isso em uma aplicação CDK:
import { MyApi } from '@my-scope/common-constructs';
export class ExampleStack extends Stack { constructor(scope: Construct, id: string) { // Add the api to your stack const api = new MyApi(this, 'MyApi', { integrations: MyApi.defaultIntegrations(this).build(), }); }}Isso configura:
- Uma função AWS Lambda para cada operação na aplicação FastAPI
- API Gateway HTTP/REST API como gatilho da função
- Funções e permissões IAM
- Grupo de logs do CloudWatch
- Configuração de rastreamento X-Ray
- Namespace de métricas do CloudWatch
Os módulos Terraform para implantar sua API estão na pasta common/terraform. Você pode usar isso em uma configuração Terraform.
O módulo da API prepara seu zip de implantação Lambda em um bucket S3 de ativos compartilhado — veja o guia de infraestrutura Terraform para detalhes. Instancie o módulo core/asset-bucket uma vez por implantação e passe sua saída bucket_name para cada módulo de API / Lambda através da entrada asset_bucket_name:
module "asset_bucket" { source = "../../common/terraform/src/core/asset-bucket"}
module "my_api" { source = "../../common/terraform/src/app/apis/my-api"
asset_bucket_name = module.asset_bucket.bucket_name
# Environment variables for the Lambda function env = { ENVIRONMENT = var.environment LOG_LEVEL = "INFO" }
# Additional IAM policies if needed additional_iam_policy_statements = [ # Add any additional permissions your API needs ]
tags = local.common_tags}Isso configura:
- Uma função AWS Lambda que serve todas as rotas FastAPI
- API Gateway HTTP/REST API como gatilho da função
- Funções e permissões IAM
- Grupo de logs do CloudWatch
- Configuração de rastreamento X-Ray
- Configuração CORS
O módulo Terraform fornece várias saídas que você pode usar:
# Access the API endpointoutput "api_url" { value = module.my_api.stage_invoke_url}
# Access Lambda function detailsoutput "lambda_function_name" { value = module.my_api.lambda_function_name}
# Access IAM role for granting additional permissionsoutput "lambda_execution_role_arn" { value = module.my_api.lambda_execution_role_arn}Você pode personalizar as configurações CORS passando variáveis para o módulo:
module "my_api" { source = "../../common/terraform/src/app/apis/my-api"
asset_bucket_name = module.asset_bucket.bucket_name
# Custom CORS configuration cors_allow_origins = ["https://myapp.com", "https://staging.myapp.com"] cors_allow_methods = ["GET", "POST", "PUT", "DELETE"] cors_allow_headers = [ "authorization", "content-type", "x-custom-header" ]
tags = local.common_tags}Para APIs REST, o construto gerado associa um Web ACL do AWS WAFv2 com o estágio do API Gateway por padrão. O Web ACL usa o conjunto de regras padrão gerenciado pela AWS (AWSManagedRulesCommonRuleSet e AWSManagedRulesKnownBadInputsRuleSet), fornecendo proteção contra explorações web comuns, incluindo o OWASP Top 10. Os logs de solicitações do WAF são gravados em um grupo do CloudWatch Logs.
Você pode editar o construto rest-api gerado para adicionar, remover ou ajustar regras (por exemplo, para adicionar regras baseadas em taxa ou grupos de regras gerenciadas adicionais).
Para desativar (por exemplo, para anexar seu próprio Web ACL), defina enableWaf como false:
const api = new MyApi(this, 'MyApi', { integrations: MyApi.defaultIntegrations(this).build(), enableWaf: false,});Para desativar (por exemplo, para anexar seu próprio Web ACL), defina enable_waf como false:
module "my_api" { source = "../../common/terraform/src/app/apis/my-api"
asset_bucket_name = module.asset_bucket.bucket_name enable_waf = false}Log de acesso
Seção intitulada “Log de acesso”Para APIs REST, a infraestrutura gerada habilita o registro de acesso por padrão, escrevendo uma linha JSON estruturada por requisição em um grupo dedicado do CloudWatch Logs. O grupo de logs é criptografado com uma chave KMS gerenciada pelo cliente e retido por um ano.
O API Gateway escreve logs de acesso usando uma função do CloudWatch Logs no nível da conta. Esta função é configurada na configuração AWS::ApiGateway::Account, que é um singleton por região por conta — há apenas uma função para cada API REST na região. Para gerenciar isso com segurança em várias pilhas implantadas independentemente, a infraestrutura gerada:
- Cria uma função compartilhada do CloudWatch Logs e a configura na conta apenas quando nenhuma função funcional já está definida, para que as implantações nunca sobrescrevam uma função que outra pilha possui.
- Deixa a configuração da conta intocada na desmontagem, para que destruir uma pilha nunca desabilite o registro para outras APIs REST na região.
A função da conta é gerenciada pelo construto ApiGatewayAccount, um singleton com escopo de pilha resolvido via ApiGatewayAccount.ensure(scope). O estágio de cada API REST depende dele, e a função é configurada por um recurso personalizado baseado em Lambda.
O formato do log de acesso é definido pelo construto RestApi que sua API estende. Para personalizá-lo, passe deployOptions para super no packages/common/constructs/src/app/apis/my-api.ts gerado, mantendo o tracingEnabled que o construto já define:
super(scope, id, { apiName: 'MyApi', // ... deployOptions: { tracingEnabled: true, accessLogFormat: AccessLogFormat.clf(), }, ...props,});AccessLogFormat é importado de aws-cdk-lib/aws-apigateway. Qualquer coisa que você deixar indefinida mantém o padrão do construto — um formato JSON com os campos padrão.
A função da conta é gerenciada pelo módulo core/api/api-gateway-account, que é instanciado pelo módulo de API gerado. Ele configura a conta de forma idempotente e nunca é redefinido no terraform destroy.
Você pode personalizar o formato do log de acesso editando o bloco access_log_settings no recurso aws_api_gateway_stage no módulo de API gerado.
Integrações
Seção intitulada “Integrações”Os construtos CDK de API REST/HTTP são configurados para fornecer uma interface type-safe para definir integrações para cada uma de suas operações.
Integrações Padrão
Seção intitulada “Integrações Padrão”Você pode usar o método estático defaultIntegrations para fazer uso do padrão default, que define uma função AWS Lambda individual para cada operação:
new MyApi(this, 'MyApi', { integrations: MyApi.defaultIntegrations(this).build(),});O módulo gerado já define as integrações padrão para o padrão com o qual a API foi gerada, então nenhuma configuração adicional é necessária:
module "my_api" { source = "../../common/terraform/src/app/apis/my-api"
asset_bucket_name = module.asset_bucket.bucket_name
tags = local.common_tags}Com o padrão isolated padrão, isso cria uma função Lambda por operação.
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 algumas 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 is typed to the operations defined in your APIapi.integrations.sayHello.handler.addToRolePolicy(new PolicyStatement({ effect: Effect.ALLOW, actions: [...], resources: [...],}));Se sua API usa o padrão shared, o Lambda router compartilhado é exposto como api.integrations.$router:
const api = new MyApi(this, 'MyApi', { integrations: MyApi.defaultIntegrations(this).build(),});
api.integrations.$router.handler.addEnvironment('LOG_LEVEL', 'DEBUG');Observe que $router não está mais disponível se você substituir todas as operações via withOverrides, já que nenhuma operação resta usando a integração de router padrão.
Com o padrão isolated, as saídas do módulo são mapas indexados por nome de operação, então você pode acessar os recursos de uma única operação. Por exemplo, para conceder permissões extras à função Lambda de uma operação:
# Grant additional permissions to just the sayHello operation's functionresource "aws_iam_role_policy" "say_hello_permissions" { name = "say-hello-additional-permissions" role = module.my_api.lambda_execution_role_names["sayHello"]
policy = jsonencode({ Version = "2012-10-17" Statement = [ { Effect = "Allow" Action = [ "s3:GetObject", "s3:PutObject" ] Resource = "arn:aws:s3:::my-bucket/*" } ] })}Para conceder as mesmas permissões a todas as operações, itere a saída operations:
resource "aws_iam_role_policy" "additional_permissions" { for_each = toset(module.my_api.operations)
name = "additional-api-permissions" role = module.my_api.lambda_execution_role_names[each.key]
policy = jsonencode({ Version = "2012-10-17" Statement = [ { Effect = "Allow" Action = ["s3:GetObject"] Resource = "arn:aws:s3:::my-bucket/*" } ] })}O módulo também expõe lambda_function_names, lambda_function_arns, lambda_invoke_arns, integration_ids e lambda_log_group_names como mapas indexados por nome de operação. Com o padrão shared, as saídas singulares equivalentes (lambda_execution_role_name, lambda_function_name, …) são expostas em vez disso, já que há apenas uma função.
Permissões que todas as operações precisam são melhor passadas ao módulo, que as aplica ao role de cada função:
module "my_api" { source = "../../common/terraform/src/app/apis/my-api"
asset_bucket_name = module.asset_bucket.bucket_name
additional_iam_policy_statements = [ { Effect = "Allow" Action = ["s3:GetObject"] 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, você pode usar o método withDefaultOptions. Por exemplo, se você deseja que todas as 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(),});A configuração de VPC já é suportada pelo módulo gerado — defina enable_vpc junto com vpc_id e subnet_ids, e o módulo implanta todas as funções Lambda em sua VPC atrás de um grupo de segurança compartilhado que ele cria para você:
module "my_api" { source = "../../common/terraform/src/app/apis/my-api"
asset_bucket_name = module.asset_bucket.bucket_name
# VPC configuration enable_vpc = true vpc_id = aws_vpc.main.id subnet_ids = [aws_subnet.private_a.id, aws_subnet.private_b.id]
tags = local.common_tags}Para opções que o módulo não expõe, edite o recurso aws_lambda_function no módulo Terraform gerado diretamente. Com o padrão isolated, esse único recurso é declarado for_each = local.operations, então uma edição lá se aplica a todas as operações.
Personalizando Opções Por Operação
Seção intitulada “Personalizando Opções Por Operação”Para personalizar as opções usadas para criar a integração padrão para operações específicas (sem afetar as outras), você pode usar o método withOperationOptions. Por exemplo, se você deseja aumentar o timeout da função Lambda para apenas uma operação:
const api = new MyApi(this, 'MyApi', { integrations: MyApi.defaultIntegrations(this) .withOperationOptions({ sayHello: { timeout: Duration.seconds(60), }, }) .build(),});
// The selected operations remain default integrations, so they're still typed accordingly:api.integrations.sayHello.handler.addToRolePolicy(new PolicyStatement({ ... }));As opções que você especifica são mescladas com as opções de integração padrão (e quaisquer opções definidas via withDefaultOptions). Observe que você não pode especificar opções para operações que você substituiu via withOverrides, pois estas não usam mais a integração padrão.
Você encontrará um erro de tipo se a mesma operação for alvo de ambos withOperationOptions e withOverrides, independentemente da ordem em que você os chamar.
Com o padrão isolated, o recurso da função Lambda já é por operação, então as opções podem ser variadas por nome de operação. Por exemplo, para dar a uma operação um timeout maior, edite o recurso aws_lambda_function no módulo gerado:
resource "aws_lambda_function" "api_lambda" { for_each = local.operations
# Default to 30 seconds, but allow longer for specific operations timeout = lookup({ sayHello = 60 }, each.key, 30)
# ... rest of configuration}Substituindo Integrações
Seção intitulada “Substituindo Integrações”Você também pode substituir integrações para operações específicas usando o método withOverrides. Cada substituição deve especificar uma propriedade integration que é tipada para o 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 substituir uma API getDocumentation para apontar para documentação hospedada por algum site externo, você pode fazer isso da seguinte forma:
new MyApi(this, 'MyApi', { integrations: MyApi.defaultIntegrations(this) .withOverrides({ getDocumentation: { integration: new HttpIntegration('https://example.com/documentation'), }, }) .build(),});Você também notará que a integração substituída não tem mais uma propriedade handler ao acessá-la 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, você pode fazer isso da seguinte forma:
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(),});
// Later, perhaps in another file, you can access the bucket property we defined// in a type-safe mannerapi.integrations.getFile.bucket.grantRead(...);Para apontar uma operação específica para um tipo de integração diferente, exclua-a do for_each padrão e declare sua integração separadamente. Por exemplo, para servir getDocumentation de um site externo:
# Exclude the overridden operation from the default per-operation resourceslocals { overridden_operations = ["getDocumentation"] default_operations = { for op, details in local.operations : op => details if !contains(local.overridden_operations, op) }}
# Then use local.default_operations in place of local.operations for the# aws_lambda_function, aws_iam_role, aws_apigatewayv2_integration and# aws_lambda_permission resources, and add the override:resource "aws_apigatewayv2_integration" "get_documentation" { api_id = module.http_api.api_id integration_type = "HTTP_PROXY" integration_uri = "https://example.com/documentation" integration_method = "GET"}
resource "aws_apigatewayv2_route" "get_documentation" { api_id = module.http_api.api_id route_key = local.route_key["getDocumentation"] target = "integrations/${aws_apigatewayv2_integration.get_documentation.id}"}Substituindo Autorizadores
Seção intitulada “Substituindo Autorizadores”Você também pode fornecer options em sua integração para substituir opções de método específicas, como autorizadores, por exemplo, se você deseja 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(...) // for REST, or HttpUserPoolAuthorizer for an HTTP API } }, }) .build(),});A autorização é definida na rota (HTTP API) ou método (REST API) para cada operação, então pode ser variada por nome de operação. Por exemplo, para deixar uma operação sem autenticação em uma HTTP API:
resource "aws_apigatewayv2_route" "operation_routes" { for_each = local.operations
# ... rest of configuration
authorization_type = each.key == "getDocumentation" ? "NONE" : "AWS_IAM"}Para uma REST API autenticada com IAM, adicione também uma declaração de política de recurso permitindo acesso não autenticado ao caminho dessa operação.
Integrações Explícitas
Seção intitulada “Integrações Explícitas”Se você preferir, pode optar por não usar as integrações padrão e, em vez disso, fornecer diretamente uma para cada operação. Isso é útil se, por exemplo, cada operação precisa usar um tipo diferente de integração ou você deseja receber um erro de tipo ao adicionar novas operações:
new MyApi(this, 'MyApi', { integrations: { sayHello: { integration: new LambdaIntegration(...), }, getDocumentation: { integration: new HttpIntegration(...), }, },});Substitua o for_each usado pelo padrão isolated por instanciações explícitas das funções Lambda, integrações e permissões para cada operação.
Padrão de Integração
Seção intitulada “Padrão de Integração”APIs geradas suportam dois padrões de integração:
isolatedcria uma função Lambda por operação. Esta é a opção padrão e recomendada para APIs.sharedcria um único Lambda router padrão e o reutiliza para cada operação, a menos que você substitua integrações específicas.
isolated oferece permissões e configuração mais granulares por operação, bem como melhor separação para logs e traces. shared reduz a probabilidade de encontrar cold-starts para APIs de baixo uso.
O padrão de integração pode ser alterado a qualquer momento no CDK atualizando seu construto de API. Por exemplo, definir pattern como 'shared' cria uma única função em vez de uma por integração:
export class MyApi<...> extends ... {
public static defaultIntegrations = (scope: Construct) => { ... return IntegrationBuilder.rest({ pattern: 'shared', ... }); };}Ao contrário do CDK, o padrão de integração é incorporado ao módulo gerado. Para alterar o padrão de integração:
- Exclua o módulo de API gerado anteriormente em
packages/common/terraform/src/app/apis - Execute novamente o gerador que criou sua API com o outro padrão de integração (ex:
--integrationPattern=shared)
Com o padrão isolated, o módulo lê as operações de um arquivo gerado:
locals { operations_file = "${path.module}/../../../generated/my-api/operations.json" operations = fileexists(local.operations_file) ? jsondecode(file(local.operations_file)) : {}}Este arquivo é gerado a partir da sua API, então você não precisa editá-lo manualmente. Adicionar uma operação ao código da aplicação da sua API adiciona a rota e a função lambda na próxima implantação. Ele está no .gitignore por padrão; remova a entrada se preferir fazer check-in dele.
Limite de Profundidade de Caminho da REST API no Terraform
Seção intitulada “Limite de Profundidade de Caminho da REST API no Terraform”Geração de Código
Seção intitulada “Geração de Código”Como as operações no FastAPI são definidas em Python e a infraestrutura CDK em TypeScript, instrumentamos a geração de código para fornecer metadados ao construto CDK para fornecer uma interface type-safe para integrações.
Um alvo generate:<ApiName>-metadata é adicionado ao project.json dos construtos comuns para facilitar essa geração de código, que emite um arquivo como packages/common/constructs/src/generated/my-api/metadata.gen.ts. Como isso é gerado em tempo de build, é ignorado no controle de versão.
Concedendo Acesso (Somente IAM)
Seção intitulada “Concedendo Acesso (Somente IAM)”Se você selecionou usar autenticação IAM, você pode usar o método grantInvokeAccess para conceder acesso à sua API:
api.grantInvokeAccess(myIdentityPool.authenticatedRole);# Create an IAM policy to allow invoking the APIresource "aws_iam_policy" "api_invoke_policy" { name = "MyApiInvokePolicy" description = "Policy to allow invoking the FastAPI"
policy = jsonencode({ Version = "2012-10-17" Statement = [ { Effect = "Allow" Action = "execute-api:Invoke" Resource = "${module.my_api.api_execution_arn}/*/*" } ] })}
# Attach the policy to an IAM role (e.g., for authenticated users)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}
# Or attach to an existing role by nameresource "aws_iam_role_policy_attachment" "api_invoke_access_existing" { role = "MyExistingRole" policy_arn = aws_iam_policy.api_invoke_policy.arn}As principais saídas do módulo da API que você pode usar para políticas IAM são:
module.my_api.api_execution_arn- Para conceder permissões execute-api:Invokemodule.my_api.api_arn- O ARN do API Gatewaymodule.my_api.lambda_function_arn- O ARN da função Lambda
Desenvolvimento Local
Seção intitulada “Desenvolvimento Local”O gerador configura um servidor de desenvolvimento local que você pode executar com:
pnpm nx serve <project-name>yarn nx serve <project-name>npx nx serve <project-name>bunx nx serve <project-name>Isso inicia um servidor de desenvolvimento FastAPI local com:
- Recarga automática em alterações de código
- Documentação interativa da API em
/docsou/redoc - Esquema OpenAPI em
/openapi.json
Invocando seu FastAPI
Seção intitulada “Invocando seu FastAPI”Para invocar sua API a partir de um site React, você pode usar o gerador connection.
Conexões
Seção intitulada “Conexões”Use o gerador connection para integrar este projeto com outros em seu workspace. As seguintes conexões envolvem este projeto: