Aller au contenu

API TypeScript Smithy

Filter this guidePick generator option values to hide sections that don't apply.

Smithy est un langage de définition d’interface indépendant du protocole pour créer des API de manière pilotée par modèle.

Le générateur d’API TypeScript Smithy crée une nouvelle API utilisant Smithy pour la définition du service, et le Smithy TypeScript Server SDK pour l’implémentation. Le générateur fournit une infrastructure en tant que code CDK ou Terraform pour déployer votre service sur AWS Lambda, exposé via une API REST AWS API Gateway. Il offre un développement d’API type-safe avec génération automatique de code à partir des modèles Smithy. Le gestionnaire généré utilise AWS Lambda Powertools pour TypeScript pour l’observabilité, incluant la journalisation, le traçage AWS X-Ray et les métriques CloudWatch

Vous pouvez générer une nouvelle API TypeScript Smithy de deux manières :

Terminal window
pnpm nx g @aws/nx-plugin:ts#api --framework=smithy
Vous pouvez également effectuer une simulation pour voir quels fichiers seraient modifiés
Terminal window
pnpm nx g @aws/nx-plugin:ts#api --framework=smithy --dry-run
ParamètreTypePar défautDescription
name Requisstring-Le nom de l'API (requis). Utilisé pour générer les noms de classes et les chemins de fichiers.
framework trpc | smithytrpcLe framework d'API à utiliser.
namespace string-L'espace de noms pour l'API Smithy (applicable uniquement pour le framework smithy). Par défaut, correspond à la portée de votre monorepo
integrationPattern isolated | sharedisolatedComment les intégrations API Gateway sont générées pour l'API. Choisissez entre isolated (par défaut) et shared.
auth iam | cognito | customiamLa méthode utilisée pour s'authentifier auprès de votre API. Choisissez entre iam (par défaut), cognito ou custom.
directory stringpackagesLe répertoire dans lequel stocker l'application.
subDirectory string-Le sous-répertoire dans lequel le projet est placé. Par défaut, il s'agit du nom du projet.
iac inherit | cdk | terraforminheritLe fournisseur IaC préféré. Par défaut, celui-ci est hérité de votre sélection initiale.
infra rest-lambda | http-lambda | nonerest-lambdaLe type d'infrastructure à utiliser pour déployer cette API.
preferInstallDependencies booleantrueIndique s'il faut privilégier l'installation des dépendances après l'exécution du générateur. Définir à false pour différer l'installation lors de l'exécution de plusieurs générateurs en lot (une installation s'exécute quand même si nécessaire pour que les générateurs suivants puissent calculer le graphe de projet Nx) ; installer une seule fois à la fin.

Le générateur crée deux projets liés dans le répertoire <directory>/<api-name> :

  • Répertoiremodel/ Projet de modèle Smithy
    • package.json Manifeste du projet définissant le nom du package et les dépendances
    • project.json Configuration du projet et cibles de build
    • smithy-build.json Configuration de build Smithy
    • ssdk.rolldown.config.mjs Bundle le TypeScript Server SDK généré
    • Répertoiresrc/
      • main.smithy Définition principale du service
      • Répertoireoperations/
        • echo.smithy Exemple de définition d’opération
  • Répertoirebackend/ Implémentation backend TypeScript
    • project.json Configuration du projet et cibles de build
    • rolldown.config.ts Configuration du bundle
    • Répertoiresrc/
      • handler.ts Gestionnaire AWS Lambda
      • local-server.ts Serveur de développement local
      • service.ts Implémentation du service
      • context.ts Définition du contexte du service
      • Répertoireoperations/
        • echo.ts Exemple d’implémentation d’opération
      • Répertoiregenerated/ SDK TypeScript généré (créé pendant le build)

Puisque ce générateur crée une infrastructure en tant que code basée sur votre iac choisi, il créera un projet dans packages/common qui inclut les constructs CDK ou modules Terraform pertinents.

Le projet d’infrastructure en tant que code commun est structuré comme suit :

  • Répertoirepackages/common/constructs
    • Répertoiresrc
      • Répertoireapp/ Constructs pour l’infrastructure spécifique à un projet/générateur
        • Répertoireapis/
          • <project-name>.ts Construct CDK pour déployer votre API
      • Répertoirecore/ Constructs génériques réutilisés par les constructs dans app
        • Répertoireapi/
          • rest-api.ts Construct CDK pour déployer une API REST
          • utils.ts Utilitaires pour les constructs d’API
      • index.ts Point d’entrée exportant les constructs depuis app
    • project.json Cibles de build et configuration du projet

L’API Smithy déployée a l’architecture suivante, avec une Web ACL AWS WAFv2 devant l’étape API Gateway :

ClientWAFAPI Gateway(REST API)Lambda(Smithy Server SDK)CloudWatch(Logs, Metrics)X-Ray(Traces)

Les opérations sont définies dans des fichiers Smithy au sein du projet de modèle. La définition principale du service se trouve dans main.smithy :

$version: "2.0"
namespace your.namespace
use aws.protocols#restJson1
use smithy.framework#ValidationException
@title("YourService")
@restJson1
service YourService {
version: "1.0.0"
operations: [
Echo,
// Add your operations here
]
errors: [
ValidationException
]
}

Les opérations individuelles sont définies dans des fichiers séparés dans le répertoire operations/ :

$version: "2.0"
namespace your.namespace
@http(method: "POST", uri: "/echo")
operation Echo {
input: EchoInput
output: EchoOutput
}
structure EchoInput {
@required
message: String
foo: Integer
bar: String
}
structure EchoOutput {
@required
message: String
}

Si vous avez plusieurs API Smithy qui partagent les mêmes types de données, vous pouvez définir ces types une fois dans une bibliothèque de formes plutôt que de les dupliquer dans chaque modèle. Une bibliothèque de formes est un projet Smithy sans service — juste des formes réutilisables — dont n’importe quel nombre de projets Smithy peuvent dépendre.

Générez-en une avec le générateur smithy#project :

Terminal window
pnpm nx g @aws/nx-plugin:smithy#project --name=my-shapes --type=shapes
Vous pouvez également effectuer une simulation pour voir quels fichiers seraient modifiés
Terminal window
pnpm nx g @aws/nx-plugin:smithy#project --name=my-shapes --type=shapes --dry-run

Le modèle de votre API peut ensuite référencer ses formes avec use :

$version: "2.0"
namespace com.example.api
use com.example.shared#Customer
structure GetCustomerOutput {
@required
customer: Customer
}

Consultez le guide du projet Smithy pour savoir comment créer une bibliothèque de formes et la configurer comme dépendance du modèle de votre API.

Les implémentations d’opérations sont situées dans le répertoire src/operations/ du projet backend. Chaque opération est implémentée en utilisant les types générés à partir du TypeScript Server SDK (généré au moment du build à partir de votre modèle Smithy).

import { ServiceContext } from '../context.js';
import { Echo as EchoOperation } from '../generated/ssdk/index.js';
export const Echo: EchoOperation<ServiceContext> = async (input) => {
// Your business logic here
return {
message: `Echo: ${input.message}` // type-safe based on your Smithy model
};
};

Les opérations doivent être enregistrées dans la définition du service dans src/service.ts :

import { ServiceContext } from './context.js';
import { YourServiceService } from './generated/ssdk/index.js';
import { Echo } from './operations/echo.js';
// Import other operations here
// Register operations to the service here
export const Service: YourServiceService<ServiceContext> = {
Echo,
// Add other operations here
};

Vous pouvez définir un contexte partagé pour vos opérations dans context.ts :

export interface ServiceContext {
// Powertools tracer, logger and metrics are provided by default
tracer: Tracer;
logger: Logger;
metrics: Metrics;
// Add shared dependencies, database connections, etc.
dbClient: any;
userIdentity: string;
}

Ce contexte est passé à toutes les implémentations d’opérations et peut être utilisé pour partager des ressources comme des connexions de base de données, de la configuration ou des utilitaires de journalisation.

Le générateur configure la journalisation structurée en utilisant AWS Lambda Powertools avec injection automatique de contexte via le middleware Middy.

handler.ts
export const handler = middy<APIGatewayProxyEvent, APIGatewayProxyResult>()
.use(captureLambdaHandler(tracer))
.use(injectLambdaContext(logger))
.use(logMetrics(metrics))
.handler(lambdaHandler);

Vous pouvez accéder au logger depuis vos implémentations d’opérations via le contexte :

operations/echo.ts
import { ServiceContext } from '../context.js';
import { Echo as EchoOperation } from '../generated/ssdk/index.js';
export const Echo: EchoOperation<ServiceContext> = async (input, ctx) => {
ctx.logger.info('Your log message');
// ...
};

Le traçage AWS X-Ray est configuré automatiquement via le middleware captureLambdaHandler.

handler.ts
export const handler = middy<APIGatewayProxyEvent, APIGatewayProxyResult>()
.use(captureLambdaHandler(tracer))
.use(injectLambdaContext(logger))
.use(logMetrics(metrics))
.handler(lambdaHandler);

Vous pouvez ajouter des sous-segments personnalisés à vos traces dans vos opérations :

operations/echo.ts
import { ServiceContext } from '../context.js';
import { Echo as EchoOperation } from '../generated/ssdk/index.js';
export const Echo: EchoOperation<ServiceContext> = async (input, ctx) => {
// Creates a new subsegment
const subsegment = ctx.tracer.getSegment()?.addNewSubsegment('custom-operation');
try {
// Your logic here
} catch (error) {
subsegment?.addError(error as Error);
throw error;
} finally {
subsegment?.close();
}
};

Les métriques CloudWatch sont collectées automatiquement pour chaque requête via le middleware logMetrics.

handler.ts
export const handler = middy<APIGatewayProxyEvent, APIGatewayProxyResult>()
.use(captureLambdaHandler(tracer))
.use(injectLambdaContext(logger))
.use(logMetrics(metrics))
.handler(lambdaHandler);

Vous pouvez ajouter des métriques personnalisées dans vos opérations :

operations/echo.ts
import { MetricUnit } from '@aws-lambda-powertools/metrics';
import { ServiceContext } from '../context.js';
import { Echo as EchoOperation } from '../generated/ssdk/index.js';
export const Echo: EchoOperation<ServiceContext> = async (input, ctx) => {
ctx.metrics.addMetric("CustomMetric", MetricUnit.Count, 1);
// ...
};

Smithy fournit une gestion des erreurs intégrée. Vous pouvez définir des erreurs personnalisées dans votre modèle Smithy :

@error("client")
@httpError(400)
structure InvalidRequestError {
@required
message: String
}

Et les enregistrer dans votre opération/service :

operation MyOperation {
...
errors: [InvalidRequestError]
}

Puis les lever dans votre implémentation TypeScript :

import { InvalidRequestError } from '../generated/ssdk/index.js';
export const MyOperation: MyOperationHandler<ServiceContext> = async (input) => {
if (!input.requiredField) {
throw new InvalidRequestError({
message: "Required field is missing"
});
}
return { /* success response */ };
};

Lorsque votre API est protégée par authentification, vos opérations ont souvent besoin de savoir qui appelle. L’approche recommandée est de résoudre l’identité de l’appelant une fois dans le gestionnaire et de la transmettre via le contexte du service pour consommation par des opérations spécifiques.

Nous modéliserons le cas non autorisé comme une erreur Smithy afin qu’elle se sérialise en une réponse 403 appropriée. Ajoutez-la à votre modèle, par exemple dans model/src/operations/errors.smithy, et référencez-la sur toute opération qui nécessite une identité :

$version: "2.0"
namespace your.namespace
/// Thrown when the calling user cannot be determined
@error("client")
@httpError(403)
structure UnauthorizedError {
@required
message: String
}

Tout d’abord, exposez l’identité résolue sur le contexte du service dans src/context.ts. Nous la fournissons en tant que fonction afin que l’UnauthorizedError soit levée depuis une opération (où le Server SDK la sérialise en un 403), plutôt que depuis le gestionnaire :

import { Logger } from '@aws-lambda-powertools/logger';
import { Metrics } from '@aws-lambda-powertools/metrics';
import { Tracer } from '@aws-lambda-powertools/tracer';
export interface Identity {
sub: string;
username: string;
}
/**
* Context provided to all operations.
*/
export interface ServiceContext {
tracer: Tracer;
logger: Logger;
metrics: Metrics;
getIdentity: () => Promise<Identity>;
}

Ensuite, écrivez le résolveur dans src/identity.ts. Il lève UnauthorizedError lorsque l’appelant ne peut pas être déterminé. L’implémentation dépend de votre méthode auth sélectionnée :

auth = iam

Pour l’authentification IAM, nous recherchons l’appelant dans Cognito en utilisant le sub extrait de l’événement API Gateway :

import { CognitoIdentityProvider } from '@aws-sdk/client-cognito-identity-provider';
import type { APIGatewayProxyEvent } from 'aws-lambda';
import { Identity } from './context.js';
import { UnauthorizedError } from './generated/ssdk/index.js';
const cognito = new CognitoIdentityProvider();
export const getIdentity = async (
event: APIGatewayProxyEvent,
): Promise<Identity> => {
const cognitoAuthenticationProvider =
event.requestContext?.identity?.cognitoAuthenticationProvider;
let sub: string | undefined = undefined;
if (cognitoAuthenticationProvider) {
const providerParts = cognitoAuthenticationProvider.split(':');
sub = providerParts[providerParts.length - 1];
}
if (!sub) {
throw new UnauthorizedError({ message: 'Unable to determine calling user' });
}
const { Users } = await cognito.listUsers({
// Assumes user pool id is configured in lambda environment
UserPoolId: process.env.USER_POOL_ID!,
Limit: 1,
Filter: `sub="${sub}"`,
});
if (!Users || Users.length !== 1) {
throw new UnauthorizedError({ message: `No user found with subjectId ${sub}` });
}
return { sub, username: Users[0].Username! };
};
auth = cognito

Avec auth: 'cognito', l’autorisateur Cognito User Pools d’API Gateway vérifie le JWT que l’appelant fournit dans l’en-tête Authorization et place les revendications vérifiées sur l’événement à event.requestContext.authorizer.claims :

import type { APIGatewayProxyEvent } from 'aws-lambda';
import { Identity } from './context.js';
import { UnauthorizedError } from './generated/ssdk/index.js';
export const getIdentity = async (
event: APIGatewayProxyEvent,
): Promise<Identity> => {
const claims = event.requestContext?.authorizer?.claims as
| Record<string, string>
| undefined;
const sub = claims?.sub;
const username = claims?.username;
if (!sub || !username) {
throw new UnauthorizedError({ message: 'Unable to determine calling user' });
}
return { sub, username };
};

Ensuite, connectez le résolveur au contexte dans src/handler.ts :

import { Service } from './service.js';
import { getIdentity } from './identity.js';
// ...
const httpResponse = await serviceHandler.handle(httpRequest, {
tracer,
logger,
metrics,
getIdentity: () => getIdentity(event),
});

Nous pouvons maintenant utiliser l’identité résolue dans une opération, par exemple dans src/operations/echo.ts :

import { ServiceContext } from '../context.js';
import { Echo as EchoOperation } from '../generated/ssdk/index.js';
export const Echo: EchoOperation<ServiceContext> = async (input, ctx) => {
const identity = await ctx.getIdentity();
return { message: `${identity.username} says ${input.message}` };
};

Le projet de modèle Smithy utilise la CLI Smithy pour construire les artefacts Smithy et générer le TypeScript Server SDK :

Terminal window
pnpm nx build <model-project>

Sur macOS et Linux, la CLI est résolue par mise, que le build récupère à la demande, donc il n’y a rien à installer — elle télécharge et met en cache la version épinglée la première fois que vous construisez.

Ce processus :

  1. Compile le modèle Smithy et le valide
  2. Génère la spécification OpenAPI à partir du modèle Smithy
  3. Crée le TypeScript Server SDK avec des interfaces d’opération type-safe
  4. Produit les artefacts de build dans dist/<model-project>/build/

Le projet backend copie automatiquement le SDK généré pendant la compilation :

Terminal window
pnpm nx copy-ssdk <backend-project>

mise ne publie aucun package Windows sur npm, donc sur Windows la CLI Smithy est un prérequis que vous installez vous-même. Installez-la une fois en suivant le guide d’installation de la CLI Smithy (par exemple winget install smithy ou scoop install smithy), et assurez-vous que smithy est dans votre PATH. Un projet Smithy généré sur Windows exécute smithy directement plutôt que via mise.

Alternativement, développez dans WSL, où le build exécute le chemin Linux et mise résout la CLI pour vous — rien à installer.

Un projet généré sur Windows commit une cible compile qui invoque smithy directement, donc toute autre personne travaillant dessus — y compris sur macOS ou Linux — a besoin de la CLI Smithy dans son PATH également. Pour que ces machines résolvent la CLI via mise à la place, basculez la cible vers la commande mise comme décrit ci-dessous.

macOS et Linux résolvent la CLI via mise et Windows utilise une CLI installée globalement, mais vous pouvez choisir l’une ou l’autre sur n’importe quelle plateforme en modifiant la commande de la cible compile dans le project.json du projet de modèle.

Pour utiliser une CLI Smithy installée globalement au lieu de mise, remplacez le préfixe mise par un simple smithy :

project.json
{
"targets": {
"compile": {
"options": {
"commands": ["... npx -y mise@<version> exec smithy@<version> -- smithy build ..."]
"commands": ["... smithy build ..."]
}
}
}
}

Pour revenir à mise résolvant la CLI, restaurez le préfixe npx -y mise@<version> exec smithy@<version> --.

Le générateur configure automatiquement une cible bundle qui utilise Rolldown pour créer un package de déploiement :

Terminal window
pnpm nx bundle <project-name>

La configuration de Rolldown se trouve dans rolldown.config.ts, avec une entrée par bundle à générer. Rolldown gère la création de plusieurs bundles en parallèle s’ils sont définis.

Le générateur configure un serveur de développement local avec rechargement à chaud :

Terminal window
pnpm nx serve <backend-project>

Le générateur crée une infrastructure CDK ou Terraform basée sur votre iac sélectionné.

Le construct CDK pour déployer votre API se trouve dans le dossier common/constructs :

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(),
});
}
}

Cela configure :

  1. Une fonction AWS Lambda pour le service Smithy
  2. Une API REST API Gateway comme déclencheur de fonction
  3. Des rôles IAM et des permissions
  4. Un groupe de logs CloudWatch
  5. Une configuration de traçage X-Ray
auth = cognito

Pour les API REST, le construct généré associe par défaut une Web ACL AWS WAFv2 à l’étape API Gateway. La Web ACL utilise l’ensemble de règles par défaut géré par AWS (AWSManagedRulesCommonRuleSet et AWSManagedRulesKnownBadInputsRuleSet), offrant une protection contre les exploits web courants, y compris le Top 10 OWASP. Les journaux de requêtes WAF sont écrits dans un groupe CloudWatch Logs.

Vous pouvez modifier le construct rest-api généré pour ajouter, supprimer ou ajuster des règles (par exemple, pour ajouter des règles basées sur le taux ou des groupes de règles gérées supplémentaires).

Pour désactiver cette fonctionnalité (par exemple, pour attacher votre propre Web ACL), définissez enableWaf sur false :

const api = new MyApi(this, 'MyApi', {
integrations: MyApi.defaultIntegrations(this).build(),
enableWaf: false,
});

Pour les API REST, l’infrastructure générée active la journalisation des accès par défaut, écrivant une ligne JSON structurée par requête dans un groupe CloudWatch Logs dédié. Le groupe de logs est chiffré avec une clé KMS gérée par le client et conservé pendant un an.

API Gateway écrit les logs d’accès en utilisant un rôle CloudWatch Logs au niveau du compte. Ce rôle est configuré sur le paramètre AWS::ApiGateway::Account, qui est un singleton par région par compte — il n’y a qu’un seul rôle pour chaque API REST dans la région. Pour gérer cela en toute sécurité à travers plusieurs stacks déployées indépendamment, l’infrastructure générée :

  • Crée un rôle CloudWatch Logs partagé et le configure sur le compte uniquement lorsqu’aucun rôle fonctionnel n’est déjà défini, de sorte que les déploiements n’écrasent jamais un rôle appartenant à une autre stack.
  • Laisse le paramètre du compte intact lors du démontage, de sorte que la destruction d’une stack ne désactive jamais la journalisation pour d’autres API REST dans la région.

Le rôle du compte est géré par le construct ApiGatewayAccount, un singleton à portée de stack résolu via ApiGatewayAccount.ensure(scope). Le stage de chaque API REST en dépend, et le rôle est configuré par une ressource personnalisée basée sur Lambda.

Le format du log d’accès est défini par le construct RestApi que votre API étend. Pour le personnaliser, passez deployOptions à super dans le fichier généré packages/common/constructs/src/app/apis/my-api.ts, en conservant le tracingEnabled que le construct définit déjà :

packages/common/constructs/src/app/apis/my-api.ts
super(scope, id, {
apiName: 'MyApi',
// ...
deployOptions: {
tracingEnabled: true,
accessLogFormat: AccessLogFormat.clf(),
},
...props,
});

AccessLogFormat est importé depuis aws-cdk-lib/aws-apigateway. Tout ce que vous ne définissez pas conserve la valeur par défaut du construct — un format JSON avec les champs standard.

Les constructs CDK d’API REST/HTTP sont configurés pour fournir une interface type-safe pour définir des intégrations pour chacune de vos opérations.

Les constructs CDK fournissent un support complet d’intégration type-safe comme décrit ci-dessous.

Vous pouvez utiliser la méthode statique defaultIntegrations pour utiliser le modèle par défaut, qui définit une fonction AWS Lambda individuelle pour chaque opération :

new MyApi(this, 'MyApi', {
integrations: MyApi.defaultIntegrations(this).build(),
});

Vous pouvez accéder aux fonctions AWS Lambda sous-jacentes via la propriété integrations du construct API, de manière type-safe. Par exemple, si votre API définit une opération nommée sayHello et que vous devez ajouter des permissions à cette fonction, vous pouvez le faire comme suit :

const api = new MyApi(this, 'MyApi', {
integrations: MyApi.defaultIntegrations(this).build(),
});
// sayHello is typed to the operations defined in your API
api.integrations.sayHello.handler.addToRolePolicy(new PolicyStatement({
effect: Effect.ALLOW,
actions: [...],
resources: [...],
}));

Si votre API utilise le modèle shared, le Lambda de routeur partagé est exposé comme api.integrations.$router :

const api = new MyApi(this, 'MyApi', {
integrations: MyApi.defaultIntegrations(this).build(),
});
api.integrations.$router.handler.addEnvironment('LOG_LEVEL', 'DEBUG');

Si vous souhaitez personnaliser les options utilisées lors de la création de la fonction Lambda pour chaque intégration par défaut, vous pouvez utiliser la méthode withDefaultOptions. Par exemple, si vous souhaitez que toutes vos fonctions Lambda résident dans un Vpc :

const vpc = new Vpc(this, 'Vpc', ...);
new MyApi(this, 'MyApi', {
integrations: MyApi.defaultIntegrations(this)
.withDefaultOptions({
vpc,
})
.build(),
});

Pour personnaliser les options utilisées pour créer l’intégration par défaut pour des opérations spécifiques (sans affecter les autres), vous pouvez utiliser la méthode withOperationOptions. Par exemple, si vous souhaitez augmenter le timeout de la fonction Lambda pour une seule opération :

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({ ... }));

Les options que vous spécifiez sont fusionnées avec les options d’intégration par défaut (et toutes les options définies via withDefaultOptions). Notez que vous ne pouvez pas spécifier d’options pour les opérations que vous avez remplacées via withOverrides, car celles-ci n’utilisent plus l’intégration par défaut.

Vous rencontrerez une erreur de type si la même opération est ciblée à la fois par withOperationOptions et withOverrides, quel que soit l’ordre dans lequel vous les appelez.

Vous pouvez également remplacer les intégrations pour des opérations spécifiques en utilisant la méthode withOverrides. Chaque remplacement doit spécifier une propriété integration qui est typée selon le construct d’intégration CDK approprié pour l’API HTTP ou REST. La méthode withOverrides est également type-safe. Par exemple, si vous souhaitez remplacer une API getDocumentation pour pointer vers de la documentation hébergée par un site web externe, vous pourriez le faire comme suit :

new MyApi(this, 'MyApi', {
integrations: MyApi.defaultIntegrations(this)
.withOverrides({
getDocumentation: {
integration: new HttpIntegration('https://example.com/documentation'),
},
})
.build(),
});

Vous remarquerez également que l’intégration remplacée n’a plus de propriété handler lors de l’accès via api.integrations.getDocumentation.

Vous pouvez ajouter des propriétés supplémentaires à une intégration qui seront également typées en conséquence, permettant à d’autres types d’intégration d’être abstraits tout en restant type-safe, par exemple si vous avez créé une intégration S3 pour une API REST et souhaitez plus tard référencer le bucket pour une opération particulière, vous pouvez le faire comme suit :

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 manner
api.integrations.getFile.bucket.grantRead(...);

Vous pouvez également fournir des options dans votre intégration pour remplacer des options de méthode particulières telles que les autorisateurs, par exemple si vous souhaitez utiliser l’authentification Cognito pour votre opération 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(),
});

Si vous préférez, vous pouvez choisir de ne pas utiliser les intégrations par défaut et d’en fournir directement une pour chaque opération. Ceci est utile si, par exemple, chaque opération doit utiliser un type d’intégration différent ou si vous souhaitez recevoir une erreur de type lors de l’ajout de nouvelles opérations :

new MyApi(this, 'MyApi', {
integrations: {
sayHello: {
integration: new LambdaIntegration(...),
},
getDocumentation: {
integration: new HttpIntegration(...),
},
},
});

Les constructs d’API CDK générés prennent en charge deux modèles d’intégration :

  • isolated crée une fonction Lambda par opération. C’est le modèle par défaut pour les API générées.
  • shared crée un seul Lambda de routeur par défaut et le réutilise pour chaque opération sauf si vous remplacez des intégrations spécifiques.

isolated vous donne des permissions et une configuration plus granulaires par opération. shared réduit la prolifération des fonctions Lambda et des intégrations API Gateway tout en permettant des remplacements sélectifs.

Par exemple, définir pattern à 'shared' crée une seule fonction au lieu d’une par intégration :

packages/common/constructs/src/app/apis/my-api.ts
export class MyApi<...> extends ... {
public static defaultIntegrations = (scope: Construct) => {
...
return IntegrationBuilder.rest({
pattern: 'shared',
...
});
};
}

Puisque les opérations sont définies dans Smithy, nous utilisons la génération de code pour fournir des métadonnées au construct CDK pour des intégrations type-safe.

Une cible generate:<ApiName>-metadata est ajoutée au project.json des constructs communs pour faciliter cette génération de code, qui émet un fichier tel que packages/common/constructs/src/generated/my-api/metadata.gen.ts. Puisque cela est généré au moment du build, il est ignoré dans le contrôle de version.

auth = iam

Si vous avez sélectionné l’authentification IAM, vous pouvez utiliser la méthode grantInvokeAccess pour accorder l’accès à votre API :

api.grantInvokeAccess(myIdentityPool.authenticatedRole);

Pour invoquer votre API depuis un site web React, vous pouvez utiliser le générateur connection, qui fournit une génération de client type-safe à partir de votre modèle Smithy.

Utilisez le générateur connection pour intégrer ce projet avec d’autres dans votre espace de travail. Les connexions suivantes impliquent ce projet :

Smithy
React vers API SmithyAppeler une API Smithy depuis un site web React
SmithyAmazon Aurora
API Smithy vers base de données relationnelleConnecter une API Smithy à une base de données relationnelle Aurora
SmithyAmazon DynamoDB
API Smithy vers TypeScript DynamoDBConnecter une API Smithy à une table DynamoDB