Migration depuis AWS PDK
Ce guide vous accompagne dans un exemple de migration d’un projet AWS PDK vers le Nx Plugin pour AWS, tout en fournissant des conseils généraux sur le sujet.
La migration vers le Nx Plugin pour AWS offre les avantages suivants par rapport à PDK :
- Des builds plus rapides
- Une utilisation plus simple (UI et CLI)
- Convivial pour le vibe-coding (essayez notre serveur MCP !)
- Des technologies plus modernes
- Développement local d’API et de sites web
- Plus de contrôle (modifiez les fichiers fournis pour vos cas d’usage)
- Et bien plus !
Exemple de migration : Application de liste de courses
Section intitulée « Exemple de migration : Application de liste de courses »Dans ce guide, nous utiliserons l’Application de liste de courses du tutoriel PDK comme projet cible à migrer. Suivez les étapes de ce tutoriel pour créer le projet cible si vous souhaitez reproduire l’exemple.
L’application de liste de courses contient les types de projets PDK suivants :
MonorepoTsProject
TypeSafeApiProject
CloudscapeReactTsWebsiteProject
InfrastructureTsProject
Créer l’espace de travail
Section intitulée « Créer l’espace de travail »Pour commencer, nous allons créer un nouvel espace de travail pour notre projet. Bien que plus radical qu’une migration in situ, cette approche donne le résultat le plus propre. Créer un espace Nx équivaut à utiliser le MonorepoTsProject
de PDK :
npx create-nx-workspace@21.4.1 shopping-list --pm=pnpm --preset=@aws/nx-plugin@0.50.0 --iacProvider=CDK --ci=skip
npx create-nx-workspace@21.4.1 shopping-list --pm=yarn --preset=@aws/nx-plugin@0.50.0 --iacProvider=CDK --ci=skip
npx create-nx-workspace@21.4.1 shopping-list --pm=npm --preset=@aws/nx-plugin@0.50.0 --iacProvider=CDK --ci=skip
npx create-nx-workspace@21.4.1 shopping-list --pm=bun --preset=@aws/nx-plugin@0.50.0 --iacProvider=CDK --ci=skip
Ouvrez le répertoire shopping-list
créé par cette commande dans votre IDE préféré.
Migrer l’API
Section intitulée « Migrer l’API »Le TypeSafeApiProject
utilisé dans l’application de liste de courses exploitait :
- Smithy comme langage de modélisation
- TypeScript pour l’implémentation des opérations
- La génération de hooks TypeScript pour l’intégration avec un site React
Nous pouvons donc utiliser le générateur ts#smithy-api
pour fournir une fonctionnalité équivalente.
Générer une API Smithy TypeScript
Section intitulée « Générer une API Smithy TypeScript »Exécutez le générateur ts#smithy-api
pour configurer votre projet d’API dans packages/api
:
- Installez le Nx Console VSCode Plugin si ce n'est pas déjà fait
- Ouvrez la console Nx dans VSCode
- Cliquez sur
Generate (UI)
dans la section "Common Nx Commands" - Recherchez
@aws/nx-plugin - ts#smithy-api
- Remplissez les paramètres requis
- name: api
- namespace: com.aws
- auth: IAM
- Cliquez sur
Generate
pnpm nx g @aws/nx-plugin:ts#smithy-api --name=api --namespace=com.aws --auth=IAM --no-interactive
yarn nx g @aws/nx-plugin:ts#smithy-api --name=api --namespace=com.aws --auth=IAM --no-interactive
npx nx g @aws/nx-plugin:ts#smithy-api --name=api --namespace=com.aws --auth=IAM --no-interactive
bunx nx g @aws/nx-plugin:ts#smithy-api --name=api --namespace=com.aws --auth=IAM --no-interactive
Vous pouvez également effectuer une simulation pour voir quels fichiers seraient modifiés
pnpm nx g @aws/nx-plugin:ts#smithy-api --name=api --namespace=com.aws --auth=IAM --no-interactive --dry-run
yarn nx g @aws/nx-plugin:ts#smithy-api --name=api --namespace=com.aws --auth=IAM --no-interactive --dry-run
npx nx g @aws/nx-plugin:ts#smithy-api --name=api --namespace=com.aws --auth=IAM --no-interactive --dry-run
bunx nx g @aws/nx-plugin:ts#smithy-api --name=api --namespace=com.aws --auth=IAM --no-interactive --dry-run
Vous remarquerez que cela génère un projet model
ainsi qu’un projet backend
. Le projet model
contient votre modèle Smithy, et backend
contient l’implémentation serveur.
Le backend utilise le Smithy Server Generator for TypeScript. Nous explorerons cela plus en détail ci-dessous.
Migrer le modèle Smithy
Section intitulée « Migrer le modèle Smithy »Maintenant que nous avons la structure de base pour notre projet d’API Smithy, nous pouvons migrer le modèle :
-
Supprimez les fichiers Smithy d’exemple générés dans
packages/api/model/src
-
Copiez votre modèle depuis le répertoire
packages/api/model/src/main/smithy
du projet PDK vers le répertoirepackages/api/model/src
de votre nouveau projet. -
Mettez à jour le nom du service et le namespace dans
smithy-build.json
pour correspondre à l’application PDK :smithy-build.json "plugins": {"openapi": {"service": "com.aws#MyApi",... -
Mettez à jour le service dans
main.smithy
pour ajouter l’erreurValidationException
, requise lors de l’utilisation du Smithy TypeScript Server SDK.main.smithy use smithy.framework#ValidationException/// My Shopping List API@restJson1service MyApi {version: "1.0"operations: [GetShoppingListsPutShoppingListDeleteShoppingList]errors: [BadRequestErrorNotAuthorizedErrorInternalFailureErrorValidationException]} -
Ajoutez un fichier
extensions.smithy
danspackages/api/model/src
où nous définirons un trait fournissant des informations de pagination au client généré :extensions.smithy $version: "2"namespace com.awsuse smithy.openapi#specificationExtension@trait@specificationExtension(as: "x-cursor")structure cursor {inputToken: Stringenabled: Boolean} -
Ajoutez le nouveau trait
@cursor
à l’opérationGetShoppingLists
dansget-shopping-lists.smithy
:operations/get-shopping-lists.smithy @readonly@http(method: "GET", uri: "/shopping-list")@paginated(inputToken: "nextToken", outputToken: "nextToken", pageSize: "pageSize", items: "shoppingLists")@cursor(inputToken: "nextToken")@handler(language: "typescript")operation GetShoppingLists {input := with [PaginatedInputMixin] {@httpQuery("shoppingListId")shoppingListId: ShoppingListId}Toutes les opérations
@paginated
devraient également utiliser@cursor
si vous utilisez le générateur de client fourni par le Nx Plugin pour AWS (via le générateurapi-connection
). -
Enfin, supprimez le trait
@handler
de toutes les opérations car il n’est pas supporté par le Nx Plugin pour AWS. Avects#smithy-api
, nous n’avons pas besoin des constructs CDK de fonctions lambda auto-générées et des cibles de bundling générées par ce trait, car nous utilisons un seul bundle pour toutes les fonctions lambda.
À ce stade, exécutons une build pour vérifier nos modifications de modèle et nous assurer d’avoir du code serveur généré à utiliser. Il y aura des erreurs dans le projet backend (@shopping-list/api
) que nous corrigerons ensuite.
pnpm nx run-many --target build
yarn nx run-many --target build
npx nx run-many --target build
bunx nx run-many --target build
Migrer les gestionnaires Lambda
Section intitulée « Migrer les gestionnaires Lambda »Vous pouvez considérer le projet api/backend
comme équivalent au projet api/handlers/typescript
de Type Safe API.
Une des principales différences entre Type Safe API et le générateur ts#smithy-api
est que les gestionnaires sont implémentés en utilisant le Smithy Server Generator for TypeScript, plutôt que les wrappers de gestionnaires générés par Type Safe API (trouvés dans le projet api/generated/typescript/runtime
).
Les gestionnaires lambda de l’application de liste de courses dépendent du package @aws-sdk/client-dynamodb
, installons-le d’abord :
pnpm add -w @aws-sdk/client-dynamodb
yarn add @aws-sdk/client-dynamodb
npm install --legacy-peer-deps @aws-sdk/client-dynamodb
bun install @aws-sdk/client-dynamodb
Ensuite, copions le fichier handlers/src/dynamo-client.ts
du projet PDK vers backend/src/operations
pour le rendre disponible à nos gestionnaires.
Pour migrer les gestionnaires, vous pouvez suivre ces étapes générales :
-
Copiez le gestionnaire depuis le répertoire
packages/api/handlers/typescript/src
de votre projet PDK vers le répertoirepackages/api/backend/src/operations
de votre nouveau projet. -
Supprimez les imports de
my-api-typescript-runtime
et importez plutôt le type d’opération depuis le SDK serveur TypeScript généré, ainsi que leServiceContext
par exemple :import {deleteShoppingListHandler,DeleteShoppingListChainedHandlerFunction,INTERCEPTORS,Response,LoggingInterceptor,} from 'myapi-typescript-runtime';import { DeleteShoppingList as DeleteShoppingListOperation } from '../generated/ssdk/index.js';import { ServiceContext } from '../context.js'; -
Supprimez l’export du wrapper de gestionnaire
export const handler = deleteShoppingListHandler(...INTERCEPTORS,deleteShoppingList,); -
Mettez à jour la signature de votre gestionnaire d’opération pour utiliser le SSDK :
export const deleteShoppingList: DeleteShoppingListChainedHandlerFunction = async (request) => {export const DeleteShoppingList: DeleteShoppingListOperation<ServiceContext> = async (input, ctx) => { -
Remplacez l’utilisation de
LoggingInterceptor
parctx.logger
. (S’applique aussi aux intercepteurs de métriques et de tracing) :LoggingInterceptor.getLogger(request).info('...');ctx.logger.info('...'); -
Mettez à jour les références aux paramètres d’entrée. Comme le SSDK fournit des types correspondant exactement à votre modèle Smithy (plutôt que de regrouper les paramètres de chemin/requête/header séparément du paramètre body), mettez à jour les références d’entrée en conséquence :
const shoppingListId = request.input.requestParameters.shoppingListId;const shoppingListId = input.shoppingListId; -
Supprimez l’utilisation de
Response
. Nous retournons plutôt des objets simples dans le SSDK.return Response.success({ shoppingListId });return { shoppingListId };Nous ne lançons plus ni ne retournons
Response
, à la place nous lançons les erreurs générées par le SSDK :throw Response.badRequest({ message: 'oh no' });return Response.badRequest({ message: 'oh no' });import { BadRequestError } from '../generated/ssdk/index.js';throw new BadRequestError({ message: 'oh no' }); -
Mettez à jour les imports pour utiliser la syntaxe ESM, en ajoutant l’extension
.js
aux imports relatifs. -
Ajoutez l’opération à
service.ts
service.ts import { ServiceContext } from './context.js';import { MyApiService } from './generated/ssdk/index.js';import { DeleteShoppingList } from './operations/delete-shopping-list.js';import { GetShoppingLists } from './operations/get-shopping-lists.js';import { PutShoppingList } from './operations/put-shopping-list.js';// Enregistrez les opérations pour le service iciexport const Service: MyApiService<ServiceContext> = {PutShoppingList,GetShoppingLists,DeleteShoppingList,};
Migration des gestionnaires de liste de courses
Supprimer une liste de courses
import { DeleteItemCommand } from '@aws-sdk/client-dynamodb';import { deleteShoppingListHandler, DeleteShoppingListChainedHandlerFunction, INTERCEPTORS, Response, LoggingInterceptor,} from 'myapi-typescript-runtime';import { ddbClient } from './dynamo-client';
/** * Gestionnaire type-safe pour l'opération DeleteShoppingList */export const deleteShoppingList: DeleteShoppingListChainedHandlerFunction = async (request) => { LoggingInterceptor.getLogger(request).info( 'Start DeleteShoppingList Operation', );
const shoppingListId = request.input.requestParameters.shoppingListId; await ddbClient.send( new DeleteItemCommand({ TableName: 'shopping_list', Key: { shoppingListId: { S: shoppingListId, }, }, }), );
return Response.success({ shoppingListId, });};
/** * Point d'entrée du gestionnaire AWS Lambda pour l'opération DeleteShoppingList. * La méthode deleteShoppingListHandler encapsule le gestionnaire type-safe et gère le marshalling des entrées/sorties */export const handler = deleteShoppingListHandler( ...INTERCEPTORS, deleteShoppingList,);
import { DeleteItemCommand } from '@aws-sdk/client-dynamodb';import { ddbClient } from './dynamo-client.js';import { DeleteShoppingList as DeleteShoppingListOperation } from '../generated/ssdk/index.js';import { ServiceContext } from '../context.js';
/** * Gestionnaire type-safe pour l'opération DeleteShoppingList */export const DeleteShoppingList: DeleteShoppingListOperation<ServiceContext> = async (input, ctx) => { ctx.logger.info( 'Start DeleteShoppingList Operation', );
const shoppingListId = input.shoppingListId; await ddbClient.send( new DeleteItemCommand({ TableName: 'shopping_list', Key: { shoppingListId: { S: shoppingListId!, }, }, }), );
return { shoppingListId, };};
Obtenir les listes de courses
import { DynamoDBClient, QueryCommand, QueryCommandInput, ScanCommand, ScanCommandInput } from '@aws-sdk/client-dynamodb';import { getShoppingListsHandler, GetShoppingListsChainedHandlerFunction, INTERCEPTORS, Response, LoggingInterceptor, ShoppingList,} from 'myapi-typescript-runtime';import { ddbClient } from './dynamo-client';
/** * Gestionnaire type-safe pour l'opération GetShoppingLists */export const getShoppingLists: GetShoppingListsChainedHandlerFunction = async (request) => { LoggingInterceptor.getLogger(request).info('Start GetShoppingLists Operation');
const nextToken = request.input.requestParameters.nextToken; const pageSize = request.input.requestParameters.pageSize; const shoppingListId = request.input.requestParameters.shoppingListId; const commandInput: ScanCommandInput | QueryCommandInput = { TableName: 'shopping_list', ConsistentRead: true, Limit: pageSize, ExclusiveStartKey: nextToken ? fromToken(nextToken) : undefined, ...(shoppingListId ? { KeyConditionExpression: 'shoppingListId = :shoppingListId', ExpressionAttributeValues: { ':shoppingListId': { S: request.input.requestParameters.shoppingListId!, }, }, } : {}), }; const response = await ddbClient.send(shoppingListId ? new QueryCommand(commandInput) : new ScanCommand(commandInput));
return Response.success({ shoppingLists: (response.Items || []) .map<ShoppingList>(item => ({ shoppingListId: item.shoppingListId.S!, name: item.name.S!, shoppingItems: JSON.parse(item.shoppingItems.S || '[]'), })), nextToken: response.LastEvaluatedKey ? toToken(response.LastEvaluatedKey) : undefined, });};
/** * Décoder un token sérialisé * @param token token passé à la requête paginée */const fromToken = <T>(token?: string): T | undefined => token ? (JSON.parse(Buffer.from(decodeURIComponent(token), 'base64').toString()) as T) : undefined;
/** * Encoder les détails de pagination dans un token sérialisé opaque * @param paginationToken détails du token de pagination */const toToken = <T>(paginationToken?: T): string | undefined => paginationToken ? encodeURIComponent(Buffer.from(JSON.stringify(paginationToken)).toString('base64')) : undefined;
/** * Point d'entrée du gestionnaire AWS Lambda pour l'opération GetShoppingLists. * La méthode getShoppingListsHandler encapsule le gestionnaire type-safe et gère le marshalling des entrées/sorties */export const handler = getShoppingListsHandler(...INTERCEPTORS, getShoppingLists);
import { QueryCommand, QueryCommandInput, ScanCommand, ScanCommandInput } from '@aws-sdk/client-dynamodb';import { ddbClient } from './dynamo-client.js';import { GetShoppingLists as GetShoppingListsOperation, ShoppingList } from '../generated/ssdk/index.js';import { ServiceContext } from '../context.js';
/** * Gestionnaire type-safe pour l'opération GetShoppingLists */export const GetShoppingLists: GetShoppingListsOperation<ServiceContext> = async (input, ctx) => { ctx.logger.info('Start GetShoppingLists Operation');
const nextToken = input.nextToken; const pageSize = input.pageSize; const shoppingListId = input.shoppingListId; const commandInput: ScanCommandInput | QueryCommandInput = { TableName: 'shopping_list', ConsistentRead: true, Limit: pageSize, ExclusiveStartKey: nextToken ? fromToken(nextToken) : undefined, ...(shoppingListId ? { KeyConditionExpression: 'shoppingListId = :shoppingListId', ExpressionAttributeValues: { ':shoppingListId': { S: input.shoppingListId!, }, }, } : {}), }; const response = await ddbClient.send(shoppingListId ? new QueryCommand(commandInput) : new ScanCommand(commandInput));
return { shoppingLists: (response.Items || []) .map<ShoppingList>(item => ({ shoppingListId: item.shoppingListId.S!, name: item.name.S!, shoppingItems: JSON.parse(item.shoppingItems.S || '[]'), })), nextToken: response.LastEvaluatedKey ? toToken(response.LastEvaluatedKey) : undefined, };};
/** * Décoder un token sérialisé * @param token token passé à la requête paginée */const fromToken = <T>(token?: string): T | undefined => token ? (JSON.parse(Buffer.from(decodeURIComponent(token), 'base64').toString()) as T) : undefined;
/** * Encoder les détails de pagination dans un token sérialisé opaque * @param paginationToken détails du token de pagination */const toToken = <T>(paginationToken?: T): string | undefined => paginationToken ? encodeURIComponent(Buffer.from(JSON.stringify(paginationToken)).toString('base64')) : undefined;
Créer une liste de courses
import { randomUUID } from 'crypto';import { DynamoDBClient, PutItemCommand } from '@aws-sdk/client-dynamodb';import { putShoppingListHandler, PutShoppingListChainedHandlerFunction, INTERCEPTORS, Response, LoggingInterceptor,} from 'myapi-typescript-runtime';import { ddbClient } from './dynamo-client';
/** * Gestionnaire type-safe pour l'opération PutShoppingList */export const putShoppingList: PutShoppingListChainedHandlerFunction = async (request) => { LoggingInterceptor.getLogger(request).info('Start PutShoppingList Operation');
const shoppingListId = request.input.body.shoppingListId ?? randomUUID(); await ddbClient.send(new PutItemCommand({ TableName: 'shopping_list', Item: { shoppingListId: { S: shoppingListId, }, name: { S: request.input.body.name, }, shoppingItems: { S: JSON.stringify(request.input.body.shoppingItems || []), }, }, }));
return Response.success({ shoppingListId, });};
/** * Point d'entrée du gestionnaire AWS Lambda pour l'opération PutShoppingList. * La méthode putShoppingListHandler encapsule le gestionnaire type-safe et gère le marshalling des entrées/sorties */export const handler = putShoppingListHandler(...INTERCEPTORS, putShoppingList);
import { randomUUID } from 'crypto';import { PutItemCommand } from '@aws-sdk/client-dynamodb';import { ddbClient } from './dynamo-client.js';import { PutShoppingList as PutShoppingListOperation } from '../generated/ssdk/index.js';import { ServiceContext } from '../context.js';
/** * Gestionnaire type-safe pour l'opération PutShoppingList */export const PutShoppingList: PutShoppingListOperation<ServiceContext> = async (input, ctx) => { ctx.logger.info('Start PutShoppingList Operation');
const shoppingListId = input.shoppingListId ?? randomUUID(); await ddbClient.send(new PutItemCommand({ TableName: 'shopping_list', Item: { shoppingListId: { S: shoppingListId, }, name: { S: input.name!, }, shoppingItems: { S: JSON.stringify(input.shoppingItems || []), }, }, }));
return { shoppingListId, };};
Nous avons généré le projet Smithy API avec le nom api
initialement pour qu’il soit ajouté à packages/api
pour cohérence avec le projet PDK. Comme notre API Smithy définit maintenant service MyApi
au lieu de service Api
, nous devons mettre à jour toutes les instances de getApiServiceHandler
avec getMyApiServiceHandler
.
Effectuez ce changement dans handler.ts
:
import { getApiServiceHandler } from './generated/ssdk/index.js'; import { getMyApiServiceHandler } from './generated/ssdk/index.js';
process.env.POWERTOOLS_METRICS_NAMESPACE = 'Api';process.env.POWERTOOLS_SERVICE_NAME = 'Api';
const tracer = new Tracer();const logger = new Logger();const metrics = new Metrics();
const serviceHandler = getApiServiceHandler(Service); const serviceHandler = getMyApiServiceHandler(Service);
Et dans local-server.ts
:
import { getApiServiceHandler } from './generated/ssdk/index.js';import { getMyApiServiceHandler } from './generated/ssdk/index.js';
const PORT = 3001;
const tracer = new Tracer();const logger = new Logger();const metrics = new Metrics();
const serviceHandler = getApiServiceHandler(Service);const serviceHandler = getMyApiServiceHandler(Service);
De plus, mettez à jour packages/api/backend/project.json
et modifiez metadata.apiName
en my-api
:
"metadata": { "generator": "ts#smithy-api", "apiName": "api", "apiName": "my-api", "auth": "IAM", "modelProject": "@shopping-list/api-model", "ports": [3001] },
Vérifier avec une build
Section intitulée « Vérifier avec une build »Nous pouvons maintenant builder le projet pour vérifier que la migration a fonctionné jusqu’à présent :
pnpm nx run-many --target build
yarn nx run-many --target build
npx nx run-many --target build
bunx nx run-many --target build
Migrer le site web
Section intitulée « Migrer le site web »Le CloudscapeReactTsWebsiteProject
utilisé dans l’application de liste de courses configurait un site React avec CloudScape et l’authentification Cognito intégrée.
Ce type de projet s’appuyait sur create-react-app
, qui est maintenant obsolète. Pour migrer le site dans ce guide, nous utiliserons le générateur ts#react-website
, qui utilise des technologies plus modernes et maintenues, notamment Vite.
Dans le cadre de la migration, nous passerons également du React Router configuré par PDK à TanStack Router, qui ajoute une sécurité typée supplémentaire pour le routage des sites.
Générer un site React
Section intitulée « Générer un site React »Exécutez le générateur ts#react-website
pour configurer votre projet de site dans packages/website
:
- Installez le Nx Console VSCode Plugin si ce n'est pas déjà fait
- Ouvrez la console Nx dans VSCode
- Cliquez sur
Generate (UI)
dans la section "Common Nx Commands" - Recherchez
@aws/nx-plugin - ts#react-website
- Remplissez les paramètres requis
- name: website
- Cliquez sur
Generate
pnpm nx g @aws/nx-plugin:ts#react-website --name=website --no-interactive
yarn nx g @aws/nx-plugin:ts#react-website --name=website --no-interactive
npx nx g @aws/nx-plugin:ts#react-website --name=website --no-interactive
bunx nx g @aws/nx-plugin:ts#react-website --name=website --no-interactive
Vous pouvez également effectuer une simulation pour voir quels fichiers seraient modifiés
pnpm nx g @aws/nx-plugin:ts#react-website --name=website --no-interactive --dry-run
yarn nx g @aws/nx-plugin:ts#react-website --name=website --no-interactive --dry-run
npx nx g @aws/nx-plugin:ts#react-website --name=website --no-interactive --dry-run
bunx nx g @aws/nx-plugin:ts#react-website --name=website --no-interactive --dry-run
Ajouter l’authentification Cognito
Section intitulée « Ajouter l’authentification Cognito »Le générateur de site React ci-dessus n’inclut pas par défaut l’authentification Cognito comme CloudscapeReactTsWebsiteProject
, elle est ajoutée explicitement via le générateur ts#react-website#auth
.
- Installez le Nx Console VSCode Plugin si ce n'est pas déjà fait
- Ouvrez la console Nx dans VSCode
- Cliquez sur
Generate (UI)
dans la section "Common Nx Commands" - Recherchez
@aws/nx-plugin - ts#react-website#auth
- Remplissez les paramètres requis
- project: website
- cognitoDomain: shopping-list
- Cliquez sur
Generate
pnpm nx g @aws/nx-plugin:ts#react-website#auth --project=website --cognitoDomain=shopping-list --no-interactive
yarn nx g @aws/nx-plugin:ts#react-website#auth --project=website --cognitoDomain=shopping-list --no-interactive
npx nx g @aws/nx-plugin:ts#react-website#auth --project=website --cognitoDomain=shopping-list --no-interactive
bunx nx g @aws/nx-plugin:ts#react-website#auth --project=website --cognitoDomain=shopping-list --no-interactive
Vous pouvez également effectuer une simulation pour voir quels fichiers seraient modifiés
pnpm nx g @aws/nx-plugin:ts#react-website#auth --project=website --cognitoDomain=shopping-list --no-interactive --dry-run
yarn nx g @aws/nx-plugin:ts#react-website#auth --project=website --cognitoDomain=shopping-list --no-interactive --dry-run
npx nx g @aws/nx-plugin:ts#react-website#auth --project=website --cognitoDomain=shopping-list --no-interactive --dry-run
bunx nx g @aws/nx-plugin:ts#react-website#auth --project=website --cognitoDomain=shopping-list --no-interactive --dry-run
Ceci ajoute des composants React qui gèrent les redirections appropriées pour garantir que les utilisateurs se connectent via l’interface hébergée de Cognito. Cela ajoute également un construct CDK pour déployer les ressources Cognito dans packages/common/constructs
, appelé UserIdentity
.
Connecter le site à l’API
Section intitulée « Connecter le site à l’API »Dans PDK, vous pouviez passer les projets Projen configurés entre eux pour déclencher l’intégration. Cela était utilisé dans l’application de liste de courses pour configurer l’intégration du site avec l’API.
Avec le Plugin Nx pour AWS, l’intégration d’API est prise en charge via le générateur api-connection
. Utilisons ce générateur pour permettre à notre site d’appeler notre API Smithy :
- Installez le Nx Console VSCode Plugin si ce n'est pas déjà fait
- Ouvrez la console Nx dans VSCode
- Cliquez sur
Generate (UI)
dans la section "Common Nx Commands" - Recherchez
@aws/nx-plugin - api-connection
- Remplissez les paramètres requis
- sourceProject: website
- targetProject: api
- Cliquez sur
Generate
pnpm nx g @aws/nx-plugin:api-connection --sourceProject=website --targetProject=api --no-interactive
yarn nx g @aws/nx-plugin:api-connection --sourceProject=website --targetProject=api --no-interactive
npx nx g @aws/nx-plugin:api-connection --sourceProject=website --targetProject=api --no-interactive
bunx nx g @aws/nx-plugin:api-connection --sourceProject=website --targetProject=api --no-interactive
Vous pouvez également effectuer une simulation pour voir quels fichiers seraient modifiés
pnpm nx g @aws/nx-plugin:api-connection --sourceProject=website --targetProject=api --no-interactive --dry-run
yarn nx g @aws/nx-plugin:api-connection --sourceProject=website --targetProject=api --no-interactive --dry-run
npx nx g @aws/nx-plugin:api-connection --sourceProject=website --targetProject=api --no-interactive --dry-run
bunx nx g @aws/nx-plugin:api-connection --sourceProject=website --targetProject=api --no-interactive --dry-run
Ceci génère les fournisseurs de client et les cibles de build nécessaires pour que votre site puisse appeler votre API via un client TypeScript généré.
Ajouter la dépendance AWS Northstar
Section intitulée « Ajouter la dépendance AWS Northstar »Le CloudscapeReactTsWebsiteProject
incluait automatiquement une dépendance sur @aws-northstar/ui
utilisée dans notre application de liste de courses, ajoutons-la ici :
pnpm add -w @aws-northstar/ui
yarn add @aws-northstar/ui
npm install --legacy-peer-deps @aws-northstar/ui
bun install @aws-northstar/ui
Déplacer les composants et pages
Section intitulée « Déplacer les composants et pages »L’application de liste de courses a un composant appelé CreateItem
, et deux pages, ShoppingList
et ShoppingLists
. Migrons-les vers le nouveau site en ajustant quelques éléments pour utiliser TanStack Router et le générateur de client TypeScript du Plugin Nx pour AWS.
-
Copiez
packages/website/src/components/CreateItem/index.tsx
du projet PDK à l’emplacement identique dans le nouveau projet. -
Copiez
packages/website/src/pages/ShoppingLists/index.tsx
verspackages/website/src/routes/index.tsx
, carShoppingLists
est notre page d’accueil et nous utilisons le routage basé sur les fichiers avec TanStack Router. -
Copiez
packages/website/src/pages/ShoppingList/index.tsx
verspackages/website/src/routes/$shoppingListId.tsx
, carShoppingList
est la page à afficher sur la route/:shoppingListId
.
Vous aurez maintenant des erreurs de build dans votre IDE, nous devrons apporter quelques modifications supplémentaires pour les résoudre, comme décrit ci-dessous.
Migrer de React Router vers TanStack Router
Section intitulée « Migrer de React Router vers TanStack Router »Comme nous utilisons le routage basé sur les fichiers, nous pouvons utiliser le serveur de développement local pour générer automatiquement la configuration des routes. Démarrons le serveur local :
pnpm nx serve-local website
yarn nx serve-local website
npx nx serve-local website
bunx nx serve-local website
Des erreurs peuvent apparaître, mais le serveur local devrait démarrer sur le port 4200
, ainsi que le serveur Smithy local sur le port 3001
.
Suivez ces étapes dans routes/index.tsx
et routes/$shoppingListId.tsx
pour migrer vers TanStack Router :
-
Ajoutez
createFileRoute
pour enregistrer chaque route :import { createFileRoute } from "@tanstack/react-router";...export default ShoppingLists;export const Route = createFileRoute('/')({component: ShoppingLists,});import { createFileRoute } from "@tanstack/react-router";...export default ShoppingList;export const Route = createFileRoute('/$shoppingListId')({component: ShoppingList,});Après sauvegarde, les erreurs de type avec
createFileRoute
devraient disparaître. -
Remplacez le hook
useNavigate
.Mettez à jour l’import :
import { useNavigate } from 'react-router-dom';import { useNavigate } from '@tanstack/react-router';Mettez à jour les appels à
navigate
pour utiliser les routes typées :navigate(`/${cell.shoppingListId}`);navigate({to: '/$shoppingListId',params: { shoppingListId: cell.shoppingListId },}); -
Remplacez le hook
useParams
.Supprimez l’import :
import { useParams } from 'react-router-dom';Utilisez le hook fourni par la
Route
créée ci-dessus (maintenant typé) :const { shoppingListId } = useParams();const { shoppingListId } = Route.useParams();
Corriger les imports de composants
Section intitulée « Corriger les imports de composants »Comme nos routes sont moins imbriquées que dans le projet PDK, corrigeons l’import de CreateItem
dans les deux fichiers de route :
import CreateItem from "../../components/CreateItem";import CreateItem from "../components/CreateItem";
Le contexte AppLayoutContext
est aussi accessible à un emplacement légèrement différent :
import { AppLayoutContext } from "../../layouts/App";import { AppLayoutContext } from "../components/AppLayout";
Migrer vers le nouveau client TypeScript généré
Section intitulée « Migrer vers le nouveau client TypeScript généré »Nous devons maintenant utiliser le client TypeScript généré par le Plugin Nx pour AWS. Suivez ces étapes :
-
Importez le nouveau client généré au lieu de l’ancien :
import {ShoppingList,usePutShoppingList,useDeleteShoppingList,useGetShoppingLists,} from "myapi-typescript-react-query-hooks";import { ShoppingList } from "../generated/my-api/types.gen";import { useMyApi } from "../hooks/useMyApi";import { useInfiniteQuery, useMutation } from "@tanstack/react-query";Notez que
routes/$shoppingListId.tsx
importe le typeShoppingList
sous_ShoppingList
- conservez cela en important depuistypes.gen
. -
Instanciez les nouveaux hooks TanStack Query :
const getShoppingLists = useGetShoppingLists({ pageSize: PAGE_SIZE });const putShoppingList = usePutShoppingList();const deleteShoppingList = useDeleteShoppingList();const api = useMyApi();const getShoppingLists = useInfiniteQuery(api.getShoppingLists.infiniteQueryOptions({ pageSize: PAGE_SIZE },{ getNextPageParam: (p) => p.nextToken },),);const putShoppingList = useMutation(api.putShoppingList.mutationOptions());const deleteShoppingList = useMutation(api.deleteShoppingList.mutationOptions(),); -
Supprimez les wrappers
<operation>RequestContent
pour les appels d’API :await putShoppingList.mutateAsync({putShoppingListRequestContent: {name: item,},});
Migrer de TanStack Query v4 vers v5
Section intitulée « Migrer de TanStack Query v4 vers v5 »Résolvons les erreurs restantes dues aux différences entre v4 et v5 :
-
Remplacez
isLoading
parisPending
pour les mutations :putShoppingList.isLoadingputShoppingList.isPending -
L’application utilisait
InfiniteQueryTable
de@aws-northstar/ui
qui attend un type de v4. Supprimez l’erreur de type :<InfiniteQueryTablequery={getShoppingLists}query={getShoppingLists as any}
Visiter le site local
Section intitulée « Visiter le site local »Vous pouvez maintenant visiter le site local sur http://localhost:4200/
Le site devrait se charger maintenant que tout est migré ! L’application nécessite seulement une table DynamoDB shopping_list
dans la région et des credentials AWS locaux pour fonctionner.
Si ce n’est pas le cas, pas d’inquiétude, nous migrerons l’infrastructure ensuite.
Migration de la page de liste de courses
Page des listes de courses
/* eslint-disable @typescript-eslint/no-floating-promises */... (code inchangé)
/* eslint-disable @typescript-eslint/no-floating-promises */... (code inchangé)
Page d’une liste de courses
/* eslint-disable @typescript-eslint/no-floating-promises */... (code inchangé)
// routes/$shoppingListId.tsx/* eslint-disable @typescript-eslint/no-floating-promises */... (code inchangé)
Migrer l’infrastructure
Section intitulée « Migrer l’infrastructure »Le dernier projet que nous devons migrer pour notre application de liste de courses est le InfrastructureTsProject
. Il s’agit d’un projet CDK TypeScript, pour lequel l’équivalent du Nx Plugin for AWS est le générateur ts#infra
.
Tout comme les projets Projen, PDK fournissait également des constructs CDK dont dépendent ces projets. Nous allons également migrer l’application de liste de courses depuis ces constructs CDK au profit de ceux générés par le Nx Plugin for AWS.
Générer un projet d’infrastructure CDK TypeScript
Section intitulée « Générer un projet d’infrastructure CDK TypeScript »Exécutez le générateur ts#infra
pour configurer votre projet d’infrastructure dans packages/infra
:
- Installez le Nx Console VSCode Plugin si ce n'est pas déjà fait
- Ouvrez la console Nx dans VSCode
- Cliquez sur
Generate (UI)
dans la section "Common Nx Commands" - Recherchez
@aws/nx-plugin - ts#infra
- Remplissez les paramètres requis
- name: infra
- Cliquez sur
Generate
pnpm nx g @aws/nx-plugin:ts#infra --name=infra --no-interactive
yarn nx g @aws/nx-plugin:ts#infra --name=infra --no-interactive
npx nx g @aws/nx-plugin:ts#infra --name=infra --no-interactive
bunx nx g @aws/nx-plugin:ts#infra --name=infra --no-interactive
Vous pouvez également effectuer une simulation pour voir quels fichiers seraient modifiés
pnpm nx g @aws/nx-plugin:ts#infra --name=infra --no-interactive --dry-run
yarn nx g @aws/nx-plugin:ts#infra --name=infra --no-interactive --dry-run
npx nx g @aws/nx-plugin:ts#infra --name=infra --no-interactive --dry-run
bunx nx g @aws/nx-plugin:ts#infra --name=infra --no-interactive --dry-run
Migrer l’infrastructure CDK
Section intitulée « Migrer l’infrastructure CDK »L’application de liste de courses PDK instanciait les constructs suivants dans la pile d’application CDK :
DatabaseConstruct
pour la table DynamoDB stockant les listes de coursesUserIdentity
pour les ressources Cognito, importé directement depuis PDKMyApi
pour déployer l’API Smithy, qui utilisait le construct CDK TypeScript généré avec des intégrations type-safe, dépendant sous le capot du construct CDKTypeSafeRestApi
de PDK.Website
pour déployer le site web, encapsulant le construct CDKStaticWebsite
de PDK.
Nous allons maintenant migrer chacun de ces éléments vers le nouveau projet.
Copier la pile d’application
Section intitulée « Copier la pile d’application »Copiez packages/infra/src/stacks/application-stack.ts
de l’application PDK vers le même emplacement dans votre nouveau projet. Vous verrez des erreurs TypeScript que nous corrigerons plus bas.
Copier le construct Database
Section intitulée « Copier le construct Database »L’application PDK avait un construct Database
dans packages/src/constructs/database.ts
. Copiez-le vers le même emplacement dans votre nouveau projet.
Comme le Nx Plugin for AWS utilise Checkov pour les tests de sécurité, qui est un peu plus strict que PDK Nag, nous devons ajouter des suppressions :
import { suppressRules } from ':shopping-list/common-constructs';...suppressRules( this.shoppingListTable, ['CKV_AWS_28', 'CKV_AWS_119'], 'Backup and KMS key not required for this project',);
Dans application-stack.ts
, mettez à jour l’import du DatabaseConstruct
pour utiliser la syntaxe ESM :
import { DatabaseConstruct } from '../constructs/database';import { DatabaseConstruct } from '../constructs/database.js';
Migrer le construct UserIdentity
Section intitulée « Migrer le construct UserIdentity »Le construct UserIdentity
peut généralement être remplacé sans modifications en ajustant les imports.
import { UserIdentity } from "@aws/pdk/identity";import { UserIdentity } from ':shopping-list/common-constructs';...const userIdentity = new UserIdentity(this, `${id}UserIdentity`);
Notez que les constructs sous-jacents utilisés par le nouveau construct UserIdentity
proviennent directement de aws-cdk-lib
, alors que PDK utilisait @aws-cdk/aws-cognito-identitypool-alpha
.
Migrer le construct API
Section intitulée « Migrer le construct API »L’application PDK avait un construct dans constructs/apis/myapi.ts
qui instanciait un construct CDK généré par Type Safe API à partir du modèle Smithy.
En plus de ce construct, comme le projet PDK utilisait la trait @handler
, des constructs CDK de fonctions lambda générées étaient également créés.
Comme Type Safe API, le Nx Plugin for AWS fournit une sécurité de type pour les intégrations basées sur votre modèle Smithy, mais cela est réalisé de manière beaucoup plus simple et flexible. Au lieu de générer un construct CDK entier au moment de la build, seul un “métadonnées” minimales est généré, que packages/common/constructs/src/app/apis/api.ts
utilise de manière générique. Vous pouvez en apprendre plus sur l’utilisation du construct dans le guide du générateur ts#smithy-api
.
Suivez les étapes suivantes :
-
Instanciez le construct
Api
dansapplication-stack.ts
stacks/application-stack.ts import { MyApi } from "../constructs/apis/myapi";import { Api } from ':shopping-list/common-constructs';...const myapi = new MyApi(this, "MyApi", {databaseConstruct,userIdentity,});const api = new Api(this, 'MyApi', {integrations: Api.defaultIntegrations(this).build(),});Notez ici l’utilisation de
Api.defaultIntegrations(this).build()
- le comportement par défaut est de créer une fonction lambda pour chaque opération de notre API, ce qui correspond au comportement que nous avions dansmyapi.ts
. -
Accordez les permissions aux fonctions lambda pour accéder à la table DynamoDB.
Dans l’application PDK, le
DatabaseConstruct
était passé àMyApi
, qui gérait l’ajout des permissions pertinentes à chaque construct de fonction généré. Nous allons faire cela directement dansapplication-stack.ts
en accédant à la propriété type-safeintegrations
du constructApi
:stacks/application-stack.ts // Accordons un accès limité à Dynamo pour nos fonctions lambdadatabaseConstruct.shoppingListTable.grantReadData(api.integrations.getShoppingLists.handler,);[api.integrations.putShoppingList.handler,api.integrations.deleteShoppingList.handler,].forEach((f) => databaseConstruct.shoppingListTable.grantWriteData(f)); -
Accordez les permissions aux utilisateurs authentifiés pour invoquer l’API.
Dans
myapi.ts
de l’application PDK, les utilisateurs authentifiés avaient également des permissions IAM pour invoquer l’API. Nous ferons l’équivalent dansapplication-stack.ts
:stacks/application-stack.ts api.grantInvokeAccess(userIdentity.identityPool.authenticatedRole);
Migrer le construct Website
Section intitulée « Migrer le construct Website »Enfin, ajoutez le construct Website
de packages/common/constructs/src/app/static-websites/website.ts
à application-stack.ts
, car il est l’équivalent du packages/infra/src/constructs/websites/website.ts
de l’application PDK.
import { Website } from "../constructs/websites/website";import { Website } from ':shopping-list/common-constructs';...new Website(this, "Website", { userIdentity, myapi,});new Website(this, 'Website');
Notez que nous ne passons pas l’identité ou l’API au site web - la configuration runtime est gérée dans chaque construct fourni par le Nx Plugin for AWS, où UserIdentity
et Api
enregistrent les valeurs nécessaires, et Website
se charge de les déployer dans /runtime-config.json
sur votre site web statique.
Compilons le projet maintenant que nous avons migré toutes les parties pertinentes du code vers notre nouveau projet.
pnpm nx run-many --target build
yarn nx run-many --target build
npx nx run-many --target build
bunx nx run-many --target build
Déployer
Section intitulée « Déployer »Maintenant que nous avons migré entièrement notre base de code, nous pouvons envisager son déploiement. Deux approches sont possibles à ce stade.
Ressources entièrement nouvelles (Simple)
Section intitulée « Ressources entièrement nouvelles (Simple) »L’approche la plus simple consiste à traiter ceci comme une application entièrement nouvelle, ce qui signifie que nous “recommencerons” avec une nouvelle table DynamoDB et un nouveau User Pool Cognito - perdant ainsi tous les utilisateurs et leurs listes de courses. Pour cette méthode, il suffit de :
-
Supprimer la table DynamoDB nommée
shopping_list
-
Déployer la nouvelle application :
Terminal window pnpm nx deploy infra shopping-list-infra-sandbox/*Terminal window yarn nx deploy infra shopping-list-infra-sandbox/*Terminal window npx nx deploy infra shopping-list-infra-sandbox/*Terminal window bunx nx deploy infra shopping-list-infra-sandbox/*
🎉 Et c’est terminé ! 🎉
Migrer les ressources avec état existantes sans interruption (Plus complexe)
Section intitulée « Migrer les ressources avec état existantes sans interruption (Plus complexe) »En réalité, il est plus probable que vous souhaitiez migrer les ressources AWS existantes pour qu’elles soient gérées par la nouvelle base de code, tout en évitant toute interruption de service pour vos clients.
Pour notre application de liste de courses, les ressources avec état importantes sont la table DynamoDB contenant les listes des utilisateurs, et le User Pool contenant les détails des utilisateurs enregistrés. Notre plan global sera de conserver ces deux ressources clés et de les déplacer pour qu’elles soient gérées par notre nouveau stack, puis de mettre à jour le DNS pour pointer vers le nouveau site web (et l’API si exposée aux clients).
-
Mettez à jour votre nouvelle application pour référencer les ressources existantes que vous souhaitez conserver.
Pour l’application de liste de courses, nous faisons cela pour la table DynamoDB :
constructs/database.ts this.shoppingListTable = new Table(this, 'ShoppingList', {...this.shoppingListTable = Table.fromTableName(this,'ShoppingList','shopping_list',);Et pour le User Pool Cognito :
packages/common/constructs/src/core/user-identity.ts this.userPool = this.createUserPool();this.userPool = UserPool.fromUserPoolId(this,'UserPool','<your-user-pool-id>',); -
Construisez et déployez la nouvelle application :
Terminal window pnpm nx run-many --target buildTerminal window yarn nx run-many --target buildTerminal window npx nx run-many --target buildTerminal window bunx nx run-many --target buildTerminal window pnpm nx deploy infra shopping-list-infra-sandbox/*Terminal window yarn nx deploy infra shopping-list-infra-sandbox/*Terminal window npx nx deploy infra shopping-list-infra-sandbox/*Terminal window bunx nx deploy infra shopping-list-infra-sandbox/*Maintenant, notre nouvelle application est déployée en référençant les ressources existantes, sans encore recevoir de trafic.
-
Effectuez des tests d’intégration complets pour vérifier le bon fonctionnement. Pour l’application de liste de courses, chargez le site et vérifiez que vous pouvez vous connecter et créer, voir, modifier et supprimer des listes.
-
Revenez en arrière sur les changements référençant les ressources existantes dans votre nouvelle application, mais ne les déployez pas encore :
constructs/database.ts this.shoppingListTable = new Table(this, 'ShoppingList', {...this.shoppingListTable = Table.fromTableName(this,'ShoppingList','shopping_list',);Et pour le User Pool Cognito :
packages/common/constructs/src/core/user-identity.ts this.userPool = this.createUserPool();this.userPool = UserPool.fromUserPoolId(this,'UserPool','<your-user-pool-id>',);Puis exécutez une build :
Terminal window pnpm nx run-many --target buildTerminal window yarn nx run-many --target buildTerminal window npx nx run-many --target buildTerminal window bunx nx run-many --target build -
Utilisez
cdk import
dans le dossierpackages/infra
de votre nouvelle application pour voir quelles ressources doivent être importées :Nouvelle application cd packages/infrapnpm exec cdk import shopping-list-infra-sandbox/Application --forceSuivez les invites en appuyant sur Entrée. L’import échouera car les ressources sont gérées par un autre stack - c’est normal, nous voulions juste confirmer les ressources à conserver. Vous verrez un résultat comme ceci :
Fenêtre de terminal shopping-list-infra-sandbox/Application/ApplicationUserIdentity/UserPool/smsRole/Resource (AWS::IAM::Role): saisir RoleName (vide pour ignorer)shopping-list-infra-sandbox/Application/ApplicationUserIdentity/UserPool/Resource (AWS::Cognito::UserPool): saisir UserPoolId (vide pour ignorer)shopping-list-infra-sandbox/Application/Database/ShoppingList/Resource (AWS::DynamoDB::Table): importer avec TableName=shopping_list (o/n) oCeci indique qu’il y a en réalité 3 ressources à importer dans notre nouveau stack.
-
Mettez à jour votre ancien projet PDK pour définir
RemovalPolicy
surRETAIN
pour les ressources identifiées. Actuellement c’est le comportement par défaut pour le User Pool et la table DynamoDB, mais nous devons l’ajouter pour le rôle SMS découvert :application-stack.ts const userIdentity = new UserIdentity(this, `${id}UserIdentity`, {userPool,});const smsRole = userIdentity.userPool.node.findAll().filter(c => CfnResource.isCfnResource(c) &&c.node.path.includes('/smsRole/'))[0] as CfnResource;smsRole.applyRemovalPolicy(RemovalPolicy.RETAIN); -
Déployez votre projet PDK pour appliquer les politiques de suppression :
Application PDK cd packages/infranpx projen deploy -
Consultez la console CloudFormation et notez les valeurs demandées lors de l’étape
cdk import
:- L’ID du User Pool, ex :
us-west-2_XXXXX
- Le nom du rôle SMS, ex :
infra-sandbox-UserIdentityUserPoolsmsRoleXXXXXX
- L’ID du User Pool, ex :
-
Mettez à jour votre projet PDK pour référencer les ressources existantes au lieu de les créer :
constructs/database.ts this.shoppingListTable = new Table(this, 'ShoppingList', {...this.shoppingListTable = Table.fromTableName(this,'ShoppingList','shopping_list',);Et pour le User Pool Cognito :
application-stack.ts const userPool = UserPool.fromUserPoolId(this,'UserPool','<your-user-pool-id>',);const userIdentity = new UserIdentity(this, `${id}UserIdentity`, {// Le construct PDK attend un UserPool mais fonctionne avec IUserPooluserPool: userPool as any,}); -
Déployez à nouveau votre projet PDK, ce qui signifie que les ressources ne sont plus gérées par son stack CloudFormation :
Application PDK cd packages/infranpx projen deploy -
Maintenant que les ressources ne sont plus gérées, exécutez
cdk import
dans votre nouvelle application pour effectuer l’import :Nouvelle application cd packages/infrapnpm exec cdk import shopping-list-infra-sandbox/Application --forceSaisissez les valeurs demandées, l’import devrait se terminer avec succès.
-
Déployez à nouveau la nouvelle application pour appliquer les modifications sur ces ressources désormais gérées :
Terminal window pnpm nx deploy infra shopping-list-infra-sandbox/*Terminal window yarn nx deploy infra shopping-list-infra-sandbox/*Terminal window npx nx deploy infra shopping-list-infra-sandbox/*Terminal window bunx nx deploy infra shopping-list-infra-sandbox/* -
Effectuez à nouveau des tests complets de la nouvelle application
-
Mettez à jour les enregistrements DNS pour pointer vers le nouveau site (et l’API si nécessaire).
Nous recommandons une approche progressive avec le Routage pondéré de Route53, en dirigeant progressivement le trafic vers la nouvelle application. Vous pouvez augmenter progressivement le poids jusqu’à ce que tout le trafic soit basculé.
Si vous n’avez pas de DNS et utilisez les domaines auto-générés, vous pouvez utiliser un proxy (comme une origine HTTP CloudFront ou des intégrations HTTP API Gateway).
-
Surveillez les métriques de l’ancienne application PDK pour confirmer l’absence de trafic, puis détruisez son stack CloudFormation :
Fenêtre de terminal cd packages/infranpx projen destroy
Ce processus était complexe, mais nous avons migré nos utilisateurs de manière transparente vers la nouvelle application ! 🎉🎉🎉
Nous bénéficions maintenant des avantages du Nx Plugin pour AWS par rapport au PDK :
- Builds plus rapides
- Support du développement d’API en local
- Une base de code plus agréable (essayez notre serveur MCP !)
- Un code client/serveur plus intuitif avec typage fort
- Et plus encore !
Foire aux questions
Section intitulée « Foire aux questions »Cette section fournit des conseils pour les fonctionnalités de PDK non couvertes par l’exemple ci-dessus.
En règle générale lors de la migration depuis PDK, nous recommandons de démarrer tout projet avec un espace Nx, étant donné ses similarités avec le monorepo PDK. Nous recommandons aussi d’utiliser nos générateurs comme primitives pour construire de nouveaux types.
npx create-nx-workspace@21.4.1 my-project --pm=pnpm --preset=@aws/nx-plugin --ci=skip
npx create-nx-workspace@21.4.1 my-project --pm=yarn --preset=@aws/nx-plugin --ci=skip
npx create-nx-workspace@21.4.1 my-project --pm=npm --preset=@aws/nx-plugin --ci=skip
npx create-nx-workspace@21.4.1 my-project --pm=bun --preset=@aws/nx-plugin --ci=skip
CDK Graph
Section intitulée « CDK Graph »CDK Graph construit des graphes de vos ressources CDK connectées et fournit deux plugins :
Diagram Plugin
Section intitulée « Diagram Plugin »Le CDK Graph Diagram Plugin génère des diagrammes d’architecture AWS à partir de votre infrastructure CDK.
Pour une approche déterministe similaire, une alternative viable est CDK-Dia.
Avec les avancées en IA générative, de nombreux modèles de base sont capables de créer des diagrammes de haute qualité à partir de votre infrastructure CDK. Nous recommandons d’essayer le AWS Diagram MCP Server. Consultez cet article de blog pour un guide détaillé.
Threat Composer Plugin
Section intitulée « Threat Composer Plugin »Le CDK Graph Threat Composer Plugin génère un modèle de menace initial pour Threat Composer à partir de votre code CDK.
Ce plugin fonctionnait simplement en filtrant un modèle de menace de base contenant des exemples de menaces, et en les filtrant selon les ressources utilisées par votre stack.
Si ces exemples de menaces spécifiques vous intéressent, vous pouvez copier et filtrer le modèle de menace de base, ou l’utiliser comme contexte pour aider un modèle de base à en générer un similaire.
AWS Arch
Section intitulée « AWS Arch »AWS Arch fournit des correspondances entre les ressources CloudFormation et leurs icônes d’architecture associées pour CDK Graph.
Consultez la page des icônes d’architecture AWS pour les ressources relatives aux icônes. Diagrams propose également une méthode pour créer des diagrammes en code.
Si vous utilisiez cela directement, envisagez de forker le projet et de vous en approprier la gestion !
Pipeline
Section intitulée « Pipeline »Le PDK fournissait un PDKPipelineProject
qui configurait un projet d’infrastructure CDK et utilisait un construct CDK encapsulant certaines ressources des CDK Pipelines.
Pour migrer depuis cette solution, vous pouvez utiliser directement les constructs CDK Pipelines. Cependant, en pratique, il est probablement plus simple d’utiliser des outils comme GitHub Actions ou GitLab CI/CD, où vous définissez des CDK Stages et exécutez la commande de déploiement pour l’étape appropriée directement.
PDK Nag encapsule CDK Nag et fournit un ensemble de règles spécifiques à la création de prototypes.
Pour migrer depuis PDK Nag, utilisez CDK Nag directement. Si vous avez besoin du même ensemble de règles, vous pouvez créer votre propre “pack” en suivant la documentation disponible ici.
API Type Safe
Section intitulée « API Type Safe »Les composants les plus couramment utilisés de Type Safe API sont couverts dans l’exemple de migration ci-dessus. Cependant, d’autres fonctionnalités nécessitent des détails de migration supplémentaires, décrits ci-dessous.
API modélisées avec OpenAPI
Section intitulée « API modélisées avec OpenAPI »Le plugin Nx pour AWS prend en charge les API modélisées en Smithy, mais pas celles modélisées directement en OpenAPI. Le générateur ts#smithy-api
constitue un bon point de départ que vous pouvez ensuite modifier. Vous pouvez définir votre spécification OpenAPI dans le dossier src
du projet model
au lieu de Smithy, et modifier le build.Dockerfile
pour utiliser votre outil de génération de code préféré pour les clients/serveurs s’ils ne sont pas disponibles sur NPM. Si vos outils souhaités sont sur NPM, vous pouvez simplement les installer comme dépendances de développement dans votre espace de travail Nx et les appeler directement en tant que cibles de build Nx.
Pour les backends type-safe modélisés en OpenAPI, vous pouvez envisager d’utiliser l’un des générateurs de serveur OpenAPI Generator. Ceux-ci ne génèrent pas directement pour AWS Lambda, mais vous pouvez utiliser AWS Lambda Web Adapter pour combler ce manque dans de nombreux cas.
Pour les clients TypeScript, vous pouvez utiliser le générateur ts#react-website
et le générateur api-connection
avec un exemple ts#smithy-api
pour voir comment les clients sont générés et intégrés à un site web. Cela configure des cibles de build qui génèrent des clients en invoquant nos générateurs open-api#ts-client
ou open-api#ts-hooks
. Vous pouvez utiliser ces générateurs vous-même en les pointant vers votre spécification OpenAPI.
Pour d’autres langages, vous pouvez également vérifier si l’un des générateurs d’OpenAPI Generator correspond à vos besoins.
Vous pouvez aussi créer un générateur sur mesure en utilisant le générateur ts#nx-generator
. Reportez-vous à la documentation de ce générateur pour les détails sur la génération de code à partir d’OpenAPI. Vous pouvez utiliser les modèles du plugin Nx pour AWS comme point de départ. Vous pouvez même vous inspirer des modèles du codebase PDK, en notant que la structure de données utilisée diffère légèrement de celle du plugin Nx pour AWS.
API modélisées avec TypeSpec
Section intitulée « API modélisées avec TypeSpec »Pour TypeSpec, la section précédente sur OpenAPI s’applique également. Vous pouvez commencer par générer un ts#smithy-api
, installer le compilateur TypeSpec et les packages OpenAPI dans votre espace de travail Nx, puis mettre à jour la cible compile
du projet model
pour exécuter tsp compile
à la place, en veillant à ce qu’il produise une spécification OpenAPI dans le répertoire dist
.
L’approche recommandée serait d’utiliser le générateur de serveur HTTP TypeSpec pour JavaScript pour générer votre code serveur, car il fonctionne directement sur votre modèle TypeSpec.
Vous pouvez utiliser AWS Lambda Web Adapter pour exécuter le serveur généré sur AWS Lambda.
Vous pouvez aussi utiliser n’importe quelle option OpenAPI mentionnée ci-dessus.
TypeSpec possède ses propres générateurs de code client pour les trois langages supportés par Type Safe API :
La section OpenAPI ci-dessus s’applique également puisque TypeSpec peut compiler vers OpenAPI.
API modélisées avec Smithy
Section intitulée « API modélisées avec Smithy »L’exemple de migration ci-dessus décrit la migration vers l’utilisation du générateur ts#smithy-api
. Cette section couvre les options pour les backends et clients Python et Java.
Le générateur de code Smithy pour Java. Celui-ci inclut un générateur de serveur Java ainsi qu’un adaptateur pour exécuter le serveur Java généré sur AWS Lambda.
Smithy ne possède pas de générateur de serveur pour Python, vous devrez donc passer par OpenAPI. Reportez-vous à la section précédente sur les API modélisées avec OpenAPI pour des options possibles.
Le générateur de code Smithy pour Java. Celui-ci inclut un générateur de client Java.
Pour les clients Python, vous pouvez consulter Smithy Python.
Pour TypeScript, consultez Smithy TypeScript, ou utilisez la même approche que celle adoptée dans ts#smithy-api
en passant par OpenAPI (nous avons choisi cette option pour assurer la cohérence entre les API tRPC, FastAPI et Smithy via les hooks TanStack Query).
Bibliothèque de formes Smithy
Section intitulée « Bibliothèque de formes Smithy »Type Safe API fournissait un type de projet Projen nommé SmithyShapeLibraryProject
qui configurait un projet contenant des modèles Smithy réutilisables par plusieurs API basées sur Smithy.
La méthode la plus directe pour reproduire cela est la suivante :
Créer une bibliothèque de formes
Section intitulée « Créer une bibliothèque de formes »-
Créez votre bibliothèque de formes à l’aide du générateur
smithy#project
:- Installez le Nx Console VSCode Plugin si ce n'est pas déjà fait
- Ouvrez la console Nx dans VSCode
- Cliquez sur
Generate (UI)
dans la section "Common Nx Commands" - Recherchez
@aws/nx-plugin - smithy#project
- Remplissez les paramètres requis
- Cliquez sur
Generate
Terminal window pnpm nx g @aws/nx-plugin:smithy#projectTerminal window yarn nx g @aws/nx-plugin:smithy#projectTerminal window npx nx g @aws/nx-plugin:smithy#projectTerminal window bunx nx g @aws/nx-plugin:smithy#projectVous pouvez également effectuer une simulation pour voir quels fichiers seraient modifiés
Terminal window pnpm nx g @aws/nx-plugin:smithy#project --dry-runTerminal window yarn nx g @aws/nx-plugin:smithy#project --dry-runTerminal window npx nx g @aws/nx-plugin:smithy#project --dry-runTerminal window bunx nx g @aws/nx-plugin:smithy#project --dry-runSpécifiez n’importe quel nom pour l’option
serviceName
, car nous supprimerons la formeservice
. -
Remplacez le modèle par défaut dans
src
par les formes que vous souhaitez définir -
Mettez à jour
smithy-build.json
pour supprimer lesplugins
et les dépendances Maven inutilisées -
Remplacez
build.Dockerfile
par des étapes de build minimales :build.Dockerfile FROM public.ecr.aws/docker/library/node:24 AS builder# Répertoire de sortieRUN mkdir /out# Installation de Smithy CLI# https://smithy.io/2.0/guides/smithy-cli/cli_installation.htmlWORKDIR /smithyARG TARGETPLATFORMRUN if [ "$TARGETPLATFORM" = "linux/arm64" ]; then ARCH="aarch64"; else ARCH="x86_64"; fi && \mkdir -p smithy-install/smithy && \curl -L https://github.com/smithy-lang/smithy/releases/download/1.61.0/smithy-cli-linux-$ARCH.zip -o smithy-install/smithy-cli-linux-$ARCH.zip && \unzip -qo smithy-install/smithy-cli-linux-$ARCH.zip -d smithy-install && \mv smithy-install/smithy-cli-linux-$ARCH/* smithy-install/smithyRUN smithy-install/smithy/install# Copie des fichiers du projetCOPY smithy-build.json .COPY src src# Build Smithy avec montage du cache MavenRUN --mount=type=cache,target=/root/.m2/repository,id=maven-cache \smithy buildRUN cp -r build/* /out/# Export du répertoire /outFROM scratch AS exportCOPY --from=builder /out /
Utiliser la bibliothèque de formes
Section intitulée « Utiliser la bibliothèque de formes »Dans vos projets de modèle de service, effectuez les modifications suivantes pour utiliser la bibliothèque de formes :
-
Mettez à jour la cible
compile
dansproject.json
pour ajouter l’espace de travail comme contexte de build, et une dépendance sur la ciblebuild
de la bibliothèque de formesproject.json {"cache": true,"outputs": ["{workspaceRoot}/dist/{projectRoot}/build"],"executor": "nx:run-commands","options": {"commands": ["rimraf dist/packages/api/model/build","make-dir dist/packages/api/model/build","docker build --build-context workspace=. -f packages/api/model/build.Dockerfile --target export --output type=local,dest=dist/packages/api/model/build packages/api/model"],"parallel": false,"cwd": "{workspaceRoot}"},"dependsOn": ["@my-project/shapes:build"]} -
Mettez à jour le
build.Dockerfile
pour copier le répertoiresrc
de votre bibliothèque de formes. Par exemple, si la bibliothèque se trouve danspackages/shapes
:build.Dockerfile # Copie des fichiers du projetCOPY smithy-build.json .COPY src srcCOPY --from=workspace packages/shapes/src shapes -
Mettez à jour
smithy-build.json
pour ajouter le répertoire des formes à sessources
:smithy-build.json {"version": "1.0","sources": ["src/", "shapes/"],"plugins": {...}
Intercepteurs
Section intitulée « Intercepteurs »Type Safe API fournissait les intercepteurs par défaut suivants :
- Intercepteurs de logging, tracing et métriques utilisant Powertools pour AWS Lambda
- Intercepteur try-catch pour gérer les exceptions non capturées
- Intercepteur CORS pour retourner les en-têtes CORS
Le générateur ts#smithy-api
instrumente le logging, tracing et les métriques avec Powertools pour AWS Lambda en utilisant Middy. Le comportement de l’intercepteur try-catch est intégré au Smithy TypeScript SSDK, et les en-têtes CORS sont ajoutés dans handler.ts
.
Pour les intercepteurs de logging, tracing et métriques dans n’importe quel langage, utilisez Powertools pour AWS Lambda directement.
Pour migrer des intercepteurs personnalisés, nous recommandons d’utiliser les bibliothèques suivantes :
- TypeScript - Middy
- Python - Powertools for AWS Lambda Middleware Factory
- Java - Instrumentez les méthodes avant/après votre logique métier en utilisant aws-lambda-java-libs pour une approche simple, ou envisagez AspectJ pour créer votre middleware via des annotations.
Génération de documentation
Section intitulée « Génération de documentation »Type Safe API fournissait la génération de documentation via Redocly CLI. Cela peut être facilement ajouté à un projet existant après migration.
-
Installez Redocly CLI
Terminal window pnpm add -Dw @redocly/cliTerminal window yarn add -D @redocly/cliTerminal window npm install --legacy-peer-deps -D @redocly/cliTerminal window bun install -D @redocly/cli -
Ajoutez une cible de génération de documentation à votre projet
model
en utilisantredocly build-docs
, par exemple :model/project.json {..."documentation": {"cache": true,"outputs": ["{workspaceRoot}/dist/{projectRoot}/documentation"],"executor": "nx:run-commands","options": {"command": "redocly build-docs dist/packages/api/model/build/openapi/openapi.json --output=dist/packages/api/model/documentation/index.html","cwd": "{workspaceRoot}"},"dependsOn": ["compile"]}}
Vous pouvez aussi envisager les générateurs de documentation OpenAPI Generator.
Intégrations mock
Section intitulée « Intégrations mock »Type Safe API générait des mocks pour vous dans son package d’infrastructure généré.
Vous pouvez migrer vers JSON Schema Faker qui peut créer des données mock basées sur des schémas JSON. Cela peut fonctionner directement sur une spécification OpenAPI, et possède une CLI que vous pourriez exécuter dans le cadre du build de votre projet model
.
Vous pouvez mettre à jour votre infrastructure CDK pour lire le fichier JSON généré par JSON Schema Faker, et retourner l’MockIntegration
approprié pour une intégration, basé sur le metadata.gen.ts
généré (en supposant que vous ayez utilisé le générateur ts#smithy-api
).
Backends multi-langages
Section intitulée « Backends multi-langages »Type Safe API supportait l’implémentation d’API avec un mélange de langages différents dans le backend. Cela peut aussi être réalisé en fournissant des “overrides” pour les intégrations lors de l’instanciation de votre construct API dans CDK :
const pythonLambdaHandler = new Function(this, 'PythonImplementation', { runtime: Runtime.PYTHON_3_12, ...});
new MyApi(this, 'MyApi', { integrations: Api.defaultIntegrations(this) .withOverrides({ echo: { integration: new LambdaIntegration(pythonLambdaHandler), handler: pythonLambdaHandler, }, }) .build(),});
Vous devrez “stubber” votre service/router pour que votre service compile si vous utilisez ts#smithy-api
et le SDK serveur TypeScript, par exemple :
export const Service: ApiService<ServiceContext> = { ... Echo: () => { throw new Error(`Not Implemented`); },};
Validation des entrées
Section intitulée « Validation des entrées »Type Safe API ajoutait une validation native des corps de requête par API Gateway basée sur votre spécification OpenAPI, car il utilisait le construct SpecRestApi
en interne.
Avec le générateur ts#smithy-api
, la validation est effectuée par le SDK serveur lui-même. C’est la même chose pour la plupart des générateurs de serveur.
Si vous souhaitez implémenter la validation native d’API Gateway, vous pouvez le faire en modifiant packages/common/constructs/src/core/api/rest-api.ts
pour lire le schéma JSON pertinent pour le corps de requête de chaque opération depuis votre spécification OpenAPI.
API WebSocket
Section intitulée « API WebSocket »Malheureusement, il n’existe pas de chemin de migration direct pour les API WebSocket de Type Safe API utilisant API Gateway et Lambda avec un développement d’API piloté par modèle. Cependant, cette section du guide vise au moins à offrir quelques pistes.
Envisagez d’utiliser AsyncAPI pour modéliser votre API au lieu d’OpenAPI ou TypeSpec, car il est conçu pour les API asynchrones. Le modèle NodeJS AsyncAPI peut générer un backend WebSocket Node que vous pourriez héberger sur ECS par exemple.
Vous pouvez aussi considérer les événements AppSync pour l’infrastructure, et utiliser Powertools. Cet article de blog vaut le détour !
Une autre option est d’utiliser des API GraphQL avec WebSocket sur AppSync, pour lesquelles nous avons une issue GitHub que vous pouvez soutenir ! Reportez-vous au guide développeur AppSync pour les détails et des liens vers des exemples.
Vous pouvez aussi envisager de créer vos propres générateurs de code qui interprètent les mêmes extensions vendor que Type Safe API. Reportez-vous à la section API modélisées avec OpenAPI pour les détails sur la création de générateurs de code personnalisés basés sur OpenAPI. Vous pouvez trouver les modèles utilisés par Type Safe API pour les gestionnaires Lambda d’API Gateway WebSocket ici, et le client ici.
Vous pouvez aussi envisager de migrer vers le générateur ts#trpc-api
pour utiliser tRPC. Au moment de la rédaction, nous ne supportons pas encore les abonnements/streaming, mais si vous en avez besoin, ajoutez un +1 à notre issue GitHub.
Smithy est agnostique au protocole, mais ne supporte pas encore le protocole WebSocket. Reportez-vous à cette issue GitHub pour le suivi du support.
Infrastructure en Python ou Java
Section intitulée « Infrastructure en Python ou Java »Le PDK prend en charge les infrastructures CDK écrites en Python et Java. Nous ne supportons pas cela dans le Nx Plugin pour AWS à l’heure actuelle.
La voie recommandée serait soit de migrer votre infrastructure CDK vers TypeScript, soit d’utiliser nos générateurs et de migrer le package common constructs vers votre langage cible. Vous pouvez utiliser l’IA générative pour accélérer ce type de migrations, par exemple avec Amazon Q CLI. Un agent d’IA peut itérer sur la migration jusqu’à obtenir des templates CloudFormation synthétisés identiques.
Ceci s’applique également à l’infrastructure générée par Type Safe API en Python ou Java - vous pouvez traduire le construct générique rest-api.ts
du package common constructs et implémenter votre propre générateur de métadonnées pour votre langage cible (voir la section APIs modélisées avec OpenAPI).
Vous pouvez utiliser le générateur py#project
pour un projet Python de base où ajouter votre code CDK (et déplacer votre fichier cdk.json
en ajoutant les cibles pertinentes). Pour les projets Java, utilisez le plugin @nx/gradle
de Nx ou @jnxplus/nx-maven
pour Maven.
Utilisation de Projen
Section intitulée « Utilisation de Projen »Le PDK a été construit sur Projen. Projen et les générateurs Nx présentent des différences fondamentales, ce qui fait que bien qu’il soit techniquement possible de les combiner, cela constitue probablement un anti-pattern. Projen gère les fichiers projet sous forme de code, de sorte qu’ils ne peuvent pas être modifiés directement, tandis que les générateurs Nx fournissent les fichiers projet une fois, après quoi le code peut être librement modifié.
Si vous souhaitez continuer à utiliser Projen, vous pouvez implémenter vous-même vos types de projet Projen souhaités. Pour suivre les modèles du plugin Nx pour AWS, vous pouvez exécuter nos générateurs ou examiner leur code source sur GitHub afin de voir comment vos types de projet souhaités sont construits, puis implémenter les parties pertinentes en utilisant les primitives de Projen.