FastAPI
FastAPI è un framework per la creazione di API in Python.
Il generatore FastAPI crea una nuova applicazione FastAPI con configurazione dell’infrastruttura AWS CDK o Terraform. Il backend generato utilizza AWS Lambda per il deployment serverless, esposto tramite un’API AWS API Gateway. Configura AWS Lambda Powertools per l’osservabilità, inclusi logging, tracciamento AWS X-Ray e metriche Cloudwatch.
Utilizzo
Sezione intitolata “Utilizzo”Generare una FastAPI
Sezione intitolata “Generare una FastAPI”Puoi generare una nuova FastAPI in due modi:
- Installa il Nx Console VSCode Plugin se non l'hai già fatto
- Apri la console Nx in VSCode
- Clicca su
Generate (UI)nella sezione "Common Nx Commands" - Cerca
@aws/nx-plugin - py#fast-api - Compila i parametri richiesti
- Clicca su
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-apiPuoi anche eseguire una prova per vedere quali file verrebbero modificati
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-runOpzioni
Sezione intitolata “Opzioni”| Parametro | Tipo | Predefinito | Descrizione |
|---|---|---|---|
| name Obbligatorio | 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. |
| integrationPattern | string | isolated | How API Gateway integrations are generated for the API. Choose between isolated (default) and shared. |
| 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 |
Output del Generatore
Sezione intitolata “Output del Generatore”Il generatore creerà la seguente struttura del progetto nella directory <directory>/<api-name>:
- project.json Configurazione del progetto e target di build
- pyproject.toml Configurazione del progetto Python e dipendenze
- run.sh Script di bootstrap Lambda Web Adapter per avviare l’app FastAPI tramite uvicorn
Directory<module_name>
- __init__.py Inizializzazione del modulo
- init.py Configura l’app FastAPI e il middleware powertools
- main.py Implementazione dell’API
Directoryscripts
- generate_open_api.py Script per generare uno schema OpenAPI dall’app FastAPI
Infrastruttura
Sezione intitolata “Infrastruttura”Poiché questo generatore fornisce infrastruttura come codice basata sul tuo iacProvider selezionato, creerà un progetto in packages/common che include i relativi costrutti CDK o moduli Terraform.
Il progetto comune di infrastruttura come codice è strutturato come segue:
Directorypackages/common/constructs
Directorysrc
Directoryapp/ Construct per l’infrastruttura specifica di un progetto/generatore
- …
Directorycore/ Construct generici riutilizzati dai construct in
app- …
- index.ts Punto di ingresso che esporta i construct da
app
- project.json Target di build e configurazione del progetto
Directorypackages/common/terraform
Directorysrc
Directoryapp/ Moduli Terraform per l’infrastruttura specifica di un progetto/generatore
- …
Directorycore/ Moduli generici riutilizzati dai moduli in
app- …
- project.json Target di build e configurazione del progetto
Per la distribuzione della tua API, vengono generati i seguenti file:
Directorypackages/common/constructs/src
Directoryapp
Directoryapis
- <project-name>.ts Costrutto CDK per distribuire la tua API
Directorycore
Directoryapi
- http-api.ts Costrutto CDK per distribuire un’API HTTP (se hai scelto di distribuire un’API HTTP)
- rest-api.ts Costrutto CDK per distribuire un’API REST (se hai scelto di distribuire un’API REST)
- utils.ts Utilities per i costrutti API
Directorypackages/common/terraform/src
Directoryapp
Directoryapis
Directory<project-name>
- <project-name>.tf Modulo per distribuire la tua API
Directorycore
Directoryapi
Directoryhttp-api
- http-api.tf Modulo per distribuire un’API HTTP (se hai scelto di distribuire un’API HTTP)
Directoryrest-api
- rest-api.tf Modulo per distribuire un’API REST (se hai scelto di distribuire un’API REST)
Implementare la tua FastAPI
Sezione intitolata “Implementare la tua FastAPI”L’implementazione principale dell’API si trova in main.py. Qui è dove definisci le route API e le relative implementazioni. Ecco un esempio:
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=...)
@app.post("/items")@tracer.capture_methoddef create_item(item: Item): return ...Il generatore configura automaticamente diverse funzionalità:
- Integrazione con AWS Lambda Powertools per l’osservabilità
- Middleware per la gestione degli errori
- Correlazione richiesta/risposta
- Raccolta delle metriche
- Deployment AWS Lambda tramite Lambda Web Adapter con uvicorn
- Streaming type-safe (solo REST API)
Osservabilità con AWS Lambda Powertools
Sezione intitolata “Osservabilità con AWS Lambda Powertools”Logging
Sezione intitolata “Logging”Il generatore configura il logging strutturato utilizzando AWS Lambda Powertools. Puoi accedere al logger nei tuoi route handler:
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}Il logger include automaticamente:
- ID di correlazione per il tracciamento delle richieste
- Percorso e metodo della richiesta
- Informazioni sul contesto Lambda
- Indicatori di cold start
Tracciamento
Sezione intitolata “Tracciamento”Il tracciamento AWS X-Ray è configurato automaticamente. Puoi aggiungere sottosegmenti personalizzati ai tuoi trace:
from .init import app, tracer
@app.get("/items/{item_id}")@tracer.capture_methoddef read_item(item_id: int): # Crea un nuovo sottosegmento with tracer.provider.in_subsegment("fetch-item-details"): # La tua logica qui return {"item_id": item_id}Metriche
Sezione intitolata “Metriche”Le metriche CloudWatch vengono raccolte automaticamente per ogni richiesta. Puoi aggiungere metriche personalizzate:
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}Le metriche predefinite includono:
- Conteggio delle richieste
- Conteggio successi/fallimenti
- Metriche sui cold start
- Metriche per route specifica
Gestione degli Errori
Sezione intitolata “Gestione degli Errori”Il generatore include una gestione degli errori completa:
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}Le eccezioni non gestite vengono intercettate dal middleware e:
- Registrano l’eccezione completa con stack trace
- Registrano una metrica di fallimento
- Restituiscono una risposta sicura 500 al client
- Mantengono l’ID di correlazione
Streaming
Sezione intitolata “Streaming”La FastAPI generata supporta risposte in streaming out of the box quando si utilizza una REST API. L’infrastruttura è configurata per utilizzare AWS Lambda Web Adapter per eseguire la tua FastAPI tramite uvicorn all’interno di Lambda, con ResponseTransferMode.STREAM in API Gateway per tutte le operazioni REST API, il che consente allo streaming di funzionare insieme alle operazioni non in streaming.
Utilizzo di JsonStreamingResponse
Sezione intitolata “Utilizzo di JsonStreamingResponse”Il file init.py generato esporta una classe JsonStreamingResponse che fornisce streaming type-safe con una corretta generazione dello schema OpenAPI. Ciò garantisce che il generatore connection possa produrre metodi client di streaming correttamente tipizzati.
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())La classe JsonStreamingResponse:
- Serializza i modelli Pydantic nel formato JSON Lines (
application/jsonl) - Fornisce un helper
openapi_responseche genera lo schema OpenAPI corretto conitemSchema, consentendo al generatoreconnectiondi produrre metodi client di streaming type-safe
Consumo
Sezione intitolata “Consumo”Per consumare uno stream di risposte, puoi utilizzare il generatore connection che fornirà un metodo type-safe per iterare sui chunk dello stream.
Distribuire la tua FastAPI
Sezione intitolata “Distribuire la tua FastAPI”Il generatore FastAPI crea infrastruttura come codice CDK o Terraform in base al iacProvider selezionato. Puoi utilizzarlo per distribuire la tua FastAPI.
Il costrutto CDK per distribuire la tua API si trova nella cartella common/constructs. Puoi usarlo in un’applicazione CDK:
import { MyApi } from ':my-scope/common-constructs';
export class ExampleStack extends Stack { constructor(scope: Construct, id: string) { // Aggiungi l'API allo stack const api = new MyApi(this, 'MyApi', { integrations: MyApi.defaultIntegrations(this).build(), }); }}Questo configura:
- Una funzione AWS Lambda per ogni operazione nell’applicazione FastAPI
- API Gateway HTTP/REST API come trigger della funzione
- Ruoli e permessi IAM
- Log group CloudWatch
- Configurazione del tracciamento X-Ray
- Namespace per le metriche CloudWatch
I moduli Terraform per distribuire la tua API si trovano nella cartella common/terraform. Puoi usarli in una configurazione Terraform:
module "my_api" { source = "../../common/terraform/src/app/apis/my-api"
# Variabili d'ambiente per la funzione Lambda env = { ENVIRONMENT = var.environment LOG_LEVEL = "INFO" }
# Policy IAM aggiuntive se necessarie additional_iam_policy_statements = [ # Aggiungi eventuali permessi aggiuntivi richiesti dalla tua API ]
tags = local.common_tags}Questo configura:
- Una funzione AWS Lambda che serve tutte le route FastAPI
- API Gateway HTTP/REST API come trigger della funzione
- Ruoli e permessi IAM
- Log group CloudWatch
- Configurazione del tracciamento X-Ray
- Configurazione CORS
Il modulo Terraform fornisce diversi output utilizzabili:
# Accedi all'endpoint APIoutput "api_url" { value = module.my_api.stage_invoke_url}
# Accedi ai dettagli della funzione Lambdaoutput "lambda_function_name" { value = module.my_api.lambda_function_name}
# Accedi al ruolo IAM per concedere permessi aggiuntivioutput "lambda_execution_role_arn" { value = module.my_api.lambda_execution_role_arn}Puoi personalizzare le impostazioni CORS passando variabili al modulo:
module "my_api" { source = "../../common/terraform/src/app/apis/my-api"
# Configurazione CORS personalizzata 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}Integrazioni
Sezione intitolata “Integrazioni”I costrutti CDK per le API REST/HTTP sono configurati per fornire un’interfaccia type-safe per definire le integrazioni per ciascuna delle tue operazioni.
I costrutti CDK forniscono supporto completo per l’integrazione type-safe come descritto di seguito.
Integrazioni Predefinite
Sezione intitolata “Integrazioni Predefinite”Puoi utilizzare il metodo statico defaultIntegrations per sfruttare il pattern predefinito, che definisce una singola funzione AWS Lambda per ogni operazione:
new MyApi(this, 'MyApi', { integrations: MyApi.defaultIntegrations(this).build(),});I moduli Terraform utilizzano automaticamente il router pattern con una singola funzione Lambda. Nessuna configurazione aggiuntiva è necessaria:
module "my_api" { source = "../../common/terraform/src/app/apis/my-api"
# Il modulo crea automaticamente una singola funzione Lambda # che gestisce tutte le operazioni API tags = local.common_tags}Scelta di un Pattern di Integrazione
Sezione intitolata “Scelta di un Pattern di Integrazione”I costrutti API CDK generati supportano due pattern di integrazione:
isolatedcrea una funzione Lambda per operazione. Questo è il pattern predefinito per le API generate.sharedcrea un singolo router Lambda predefinito e lo riutilizza per ogni operazione a meno che non si sovrascrivano integrazioni specifiche.
isolated offre permessi e configurazione più granulari per operazione. shared riduce la proliferazione di Lambda e integrazioni API Gateway pur consentendo override selettivi.
Il supporto del generatore varia attualmente in base al backend:
ts#trpc-apisupporta siaisolatedchesharedperServerlessApiGatewayRestApieServerlessApiGatewayHttpApitramite l’opzioneintegrationPattern.py#fast-apiets#smithy-apiattualmente generanoisolated.
Puoi anche cambiare un costrutto API CDK generato esistente tra i due pattern modificando il campo pattern nel suo metodo defaultIntegrations:
return IntegrationBuilder.rest({ // Cambia questo in 'isolated' o 'shared' pattern: 'isolated', ...});I moduli Terraform utilizzano già il router pattern per impostazione predefinita, con una singola funzione Lambda che gestisce tutte le operazioni API.
Accesso alle Integrazioni
Sezione intitolata “Accesso alle Integrazioni”Puoi accedere alle funzioni AWS Lambda sottostanti tramite la proprietà integrations del costrutto API in modo type-safe. Ad esempio, se la tua API definisce un’operazione chiamata sayHello e devi aggiungere dei permessi a questa funzione, puoi farlo come segue:
const api = new MyApi(this, 'MyApi', { integrations: MyApi.defaultIntegrations(this).build(),});
// sayHello è tipizzato in base alle operazioni definite nella tua APIapi.integrations.sayHello.handler.addToRolePolicy(new PolicyStatement({ effect: Effect.ALLOW, actions: [...], resources: [...],}));Se la tua API utilizza il pattern shared, il router Lambda condiviso è esposto come api.integrations.$router:
const api = new MyApi(this, 'MyApi', { integrations: MyApi.defaultIntegrations(this).build(),});
api.integrations.$router.handler.addEnvironment('LOG_LEVEL', 'DEBUG');Con il router pattern di Terraform, esiste solo una funzione Lambda. Puoi accedervi tramite gli output del modulo:
# Concedi permessi aggiuntivi alla singola funzione 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/*" } ] })}Personalizzazione delle Opzioni Predefinite
Sezione intitolata “Personalizzazione delle Opzioni Predefinite”Se desideri personalizzare le opzioni utilizzate durante la creazione delle funzioni Lambda per ogni integrazione predefinita, puoi usare il metodo withDefaultOptions. Ad esempio, per far risiedere tutte le funzioni Lambda in una VPC:
const vpc = new Vpc(this, 'Vpc', ...);
new MyApi(this, 'MyApi', { integrations: MyApi.defaultIntegrations(this) .withDefaultOptions({ vpc, }) .build(),});Per personalizzare opzioni come la configurazione VPC, devi modificare il modulo Terraform generato. Ad esempio, per aggiungere il supporto VPC a tutte le funzioni Lambda:
# Aggiungi variabili VPCvariable "vpc_subnet_ids" { description = "Lista di ID subnet VPC per la funzione Lambda" type = list(string) default = []}
variable "vpc_security_group_ids" { description = "Lista di ID security group VPC per la funzione Lambda" type = list(string) default = []}
# Aggiorna la risorsa Lambda functionresource "aws_lambda_function" "api_lambda" { # ... configurazione esistente ...
# Aggiungi configurazione VPC vpc_config { subnet_ids = var.vpc_subnet_ids security_group_ids = var.vpc_security_group_ids }}Quindi utilizza il modulo con la configurazione VPC:
module "my_api" { source = "../../common/terraform/src/app/apis/my-api"
# Configurazione 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}Override delle Integrazioni
Sezione intitolata “Override delle Integrazioni”Puoi anche sovrascrivere le integrazioni per operazioni specifiche usando il metodo withOverrides. Ogni override deve specificare una proprietà integration tipizzata correttamente per il costrutto di integrazione CDK appropriato per l’API HTTP o REST. Il metodo withOverrides è anch’esso type-safe. Ad esempio, se desideri sovrascrivere un’API getDocumentation per puntare alla documentazione ospitata da un sito web esterno, puoi farlo come segue:
new MyApi(this, 'MyApi', { integrations: MyApi.defaultIntegrations(this) .withOverrides({ getDocumentation: { integration: new HttpIntegration('https://example.com/documentation'), }, }) .build(),});Noterai anche che l’integrazione sovrascritta non ha più una proprietà handler quando vi si accede tramite api.integrations.getDocumentation.
Puoi aggiungere proprietà aggiuntive a un’integrazione che saranno anche tipizzate di conseguenza, consentendo ad altri tipi di integrazione di essere astratti ma rimanere type-safe, ad esempio se hai creato un’integrazione S3 per un’API REST e successivamente desideri fare riferimento al bucket per una particolare operazione, puoi farlo come segue:
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(),});
// Successivamente, magari in un altro file, puoi accedere alla proprietà bucket che abbiamo definito// in modo type-safeapi.integrations.getFile.bucket.grantRead(...);Override degli Authorizer
Sezione intitolata “Override degli Authorizer”Puoi anche fornire options nella tua integrazione per sovrascrivere particolari opzioni del metodo come gli authorizer, ad esempio se desideri utilizzare l’autenticazione Cognito per la tua operazione getDocumentation:
new MyApi(this, 'MyApi', { integrations: MyApi.defaultIntegrations(this) .withOverrides({ getDocumentation: { integration: new HttpIntegration('https://example.com/documentation'), options: { authorizer: new CognitoUserPoolsAuthorizer(...) // per REST, o HttpUserPoolAuthorizer per un'API HTTP } }, }) .build(),});Integrazioni Esplicite
Sezione intitolata “Integrazioni Esplicite”Se preferisci, puoi scegliere di non utilizzare le integrazioni predefinite e invece fornirne direttamente una per ogni operazione. Questo è utile se, ad esempio, ogni operazione deve utilizzare un tipo diverso di integrazione o desideri ricevere un errore di tipo quando aggiungi nuove operazioni:
new MyApi(this, 'MyApi', { integrations: { sayHello: { integration: new LambdaIntegration(...), }, getDocumentation: { integration: new HttpIntegration(...), }, },});Per integrazioni esplicite per operazione con Terraform, dovresti modificare il modulo specifico dell’app generato per sostituire l’integrazione proxy predefinita con integrazioni specifiche per ogni operazione.
Modifica packages/common/terraform/src/app/apis/my-api/my-api.tf:
- Rimuovi le route proxy predefinite (es.
resource "aws_apigatewayv2_route" "proxy_routes") - Sostituisci la singola funzione Lambda con funzioni individuali per ogni operazione
- Crea integrazioni e route specifiche per ogni operazione, riutilizzando lo stesso bundle ZIP:
# Rimuovi la singola funzione Lambda predefinita 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 # ... resto della configurazione }
# Rimuovi l'integrazione proxy predefinita 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 # ... resto della configurazione }
# Rimuovi le route proxy predefinite 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}" # ... resto della configurazione }
# Aggiungi funzioni Lambda individuali per ogni operazione usando lo stesso bundle 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 specifico per questa operazione 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 specifico per questa operazione 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 }
# Aggiungi integrazioni specifiche per ogni operazione 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" }
# Aggiungi route specifiche per ogni operazione 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" }
# Aggiungi permessi Lambda per ogni funzione 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}/*/*" }# Rimuovi la singola funzione Lambda predefinita 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 # ... resto della configurazione }
# Rimuovi l'integrazione proxy predefinita 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 # ... resto della configurazione }
# Rimuovi le route proxy predefinite 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}" # ... resto della configurazione }
# Aggiungi funzioni Lambda individuali per ogni operazione usando lo stesso bundle 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 specifico per questa operazione 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 specifico per questa operazione 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 }
# Aggiungi risorse e metodi specifici per ogni operazione 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" }
# Aggiorna il deployment per dipendere dalle nuove integrazioni~ 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, ])) } }
# Aggiungi permessi Lambda per ogni funzione 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}/*/*" }Pattern di Integrazione
Sezione intitolata “Pattern di Integrazione”I costrutti API CDK generati supportano due pattern di integrazione:
isolatedcrea una funzione Lambda per operazione. Questo è il pattern predefinito per le API generate.sharedcrea un singolo router Lambda predefinito e lo riutilizza per ogni operazione a meno che non si sovrascrivano integrazioni specifiche.
isolated offre permessi e configurazione più granulari per operazione. shared riduce la proliferazione di Lambda e integrazioni API Gateway pur consentendo override selettivi.
Ad esempio, impostando pattern su 'shared' si crea una singola funzione invece di una per integrazione:
export class MyApi<...> extends ... {
public static defaultIntegrations = (scope: Construct) => { ... return IntegrationBuilder.rest({ pattern: 'shared', ... }); };}I moduli Terraform utilizzano automaticamente il router pattern - questo è l’approccio predefinito e unico supportato. Il modulo generato crea una singola funzione Lambda che gestisce tutte le operazioni API.
Puoi semplicemente istanziare il modulo predefinito per ottenere il router pattern:
# Router pattern predefinito - singola funzione Lambda per tutte le operazionimodule "my_api" { source = "../../common/terraform/src/app/apis/my-api"
# La singola funzione Lambda gestisce automaticamente tutte le operazioni tags = local.common_tags}Generazione del Codice
Sezione intitolata “Generazione del Codice”Poiché le operazioni in FastAPI sono definite in Python e l’infrastruttura CDK in TypeScript, strumentiamo la generazione del codice per fornire metadati al costrutto CDK e garantire un’interfaccia type-safe per le integrazioni.
Un target generate:<ApiName>-metadata viene aggiunto al project.json dei costrutti comuni per facilitare questa generazione, producendo un file come packages/common/constructs/src/generated/my-api/metadata.gen.ts. Essendo generato al momento della build, viene ignorato nel version control.
Concessione dell’Accesso (Solo IAM)
Sezione intitolata “Concessione dell’Accesso (Solo IAM)”Se hai selezionato l’autenticazione IAM, puoi usare il metodo grantInvokeAccess per concedere l’accesso alla tua API:
api.grantInvokeAccess(myIdentityPool.authenticatedRole);# Crea una policy IAM per consentire l'invocazione dell'APIresource "aws_iam_policy" "api_invoke_policy" { name = "MyApiInvokePolicy" description = "Policy per consentire l'invocazione della FastAPI"
policy = jsonencode({ Version = "2012-10-17" Statement = [ { Effect = "Allow" Action = "execute-api:Invoke" Resource = "${module.my_api.api_execution_arn}/*/*" } ] })}
# Allega la policy a un ruolo IAM (es. per utenti autenticati)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}
# O allega a un ruolo esistente per nomeresource "aws_iam_role_policy_attachment" "api_invoke_access_existing" { role = "MyExistingRole" policy_arn = aws_iam_policy.api_invoke_policy.arn}Gli output principali dal modulo API utilizzabili per le policy IAM sono:
module.my_api.api_execution_arn- Per concedere permessi execute-api:Invokemodule.my_api.api_arn- L’ARN di API Gatewaymodule.my_api.lambda_function_arn- L’ARN della funzione Lambda
Sviluppo Locale
Sezione intitolata “Sviluppo Locale”Il generatore configura un server di sviluppo locale che puoi eseguire con:
pnpm nx run my-api:serveyarn nx run my-api:servenpx nx run my-api:servebunx nx run my-api:serveQuesto avvia un server di sviluppo FastAPI locale con:
- Auto-reload alle modifiche del codice
- Documentazione API interattiva su
/docso/redoc - Schema OpenAPI su
/openapi.json
Invocare la tua FastAPI
Sezione intitolata “Invocare la tua FastAPI”Per invocare la tua API da un sito React, puoi utilizzare il generatore connection.