Pular para o conteúdo

API TypeScript do Smithy

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

Smithy é uma linguagem de definição de interface independente de protocolo para criação de APIs de forma orientada a modelos.

O gerador de API Smithy TypeScript cria uma nova API usando Smithy para definição de serviços e o Smithy TypeScript Server SDK para implementação. O gerador fornece infraestrutura como código via CDK ou Terraform para implantar seu serviço na AWS Lambda, exposto através de uma API REST do AWS API Gateway. Oferece desenvolvimento de API com tipagem segura e geração automática de código a partir de modelos Smithy. O handler gerado utiliza AWS Lambda Powertools for TypeScript para observabilidade, incluindo logging, rastreamento com AWS X-Ray e métricas no CloudWatch.

Você pode gerar uma nova API Smithy TypeScript de duas formas:

  1. Instale o Nx Console VSCode Plugin se ainda não o fez
  2. Abra o console Nx no VSCode
  3. Clique em Generate (UI) na seção "Common Nx Commands"
  4. Procure por @aws/nx-plugin - ts#api
  5. Preencha os parâmetros obrigatórios
    • framework: smithy
  6. Clique em Generate
ParâmetroTipoPadrãoDescrição
name Obrigatóriostring-O nome da API (obrigatório). Usado para gerar nomes de classes e caminhos de arquivos.
framework trpc | smithytrpcO framework de API a ser utilizado.
namespace string-O namespace para a API Smithy (aplicável apenas para o framework smithy). O padrão é o escopo do seu monorepo
integrationPattern isolated | sharedisolatedComo as integrações do API Gateway são geradas para a API. Escolha entre isolated (padrão) e shared.
auth iam | cognito | customiamO método usado para autenticar com sua API. Escolha entre iam (padrão), cognito ou custom.
directory stringpackagesO diretório para armazenar a aplicação.
subDirectory string-O subdiretório onde o projeto é colocado. Por padrão, este é o nome do projeto.
iac inherit | cdk | terraforminheritO provedor IaC preferido. Por padrão, isso é herdado da sua seleção inicial.
infra rest-lambda | http-lambda | nonerest-lambdaO tipo de infraestrutura a ser usado para implantar esta API.
preferInstallDependencies booleantrueSe deve preferir instalar dependências após a execução do gerador. Defina como false para adiar a instalação ao executar múltiplos geradores em lote (uma instalação ainda é executada se necessário para que geradores subsequentes possam calcular o grafo de projetos Nx); instale uma vez no final.

O gerador cria dois projetos relacionados no diretório <directory>/<api-name>:

  • Directorymodel/ Projeto de modelo Smithy
    • project.json Configuração do projeto e targets de build
    • smithy-build.json Configuração de build Smithy
    • build.Dockerfile Configuração Docker para construir artefatos Smithy
    • Directorysrc/
      • main.smithy Definição principal do serviço
      • Directoryoperations/
        • echo.smithy Exemplo de definição de operação
  • Directorybackend/ Implementação TypeScript do backend
    • project.json Configuração do projeto e targets de build
    • rolldown.config.ts Configuração de bundle
    • Directorysrc/
      • handler.ts Handler AWS Lambda
      • local-server.ts Servidor local de desenvolvimento
      • service.ts Implementação do serviço
      • context.ts Definição de contexto do serviço
      • Directoryoperations/
        • echo.ts Exemplo de implementação de operação
      • Directorygenerated/ SDK TypeScript gerado (criado durante o build)

Como este gerador cria infraestrutura como código baseada no iacProvider escolhido, ele criará um projeto em packages/common que inclui os constructs CDK ou módulos Terraform relevantes.

O projeto comum de infraestrutura como código é estruturado da seguinte forma:

  • Directorypackages/common/constructs
    • Directorysrc
      • Directoryapp/ Constructs para infraestrutura específica de um projeto/gerador
        • Directoryapis/
          • <project-name>.ts Construct CDK para implantar sua API
      • Directorycore/ Constructs genéricos reutilizados por constructs em app
        • Directoryapi/
          • rest-api.ts Construct CDK para implantar uma API REST
          • utils.ts Utilitários para os constructs de API
      • index.ts Ponto de entrada exportando constructs de app
    • project.json Targets de build e configuração do projeto

A API Smithy implantada possui a seguinte arquitetura, com um Web ACL AWS WAFv2 na frente do estágio do API Gateway:

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

Operações são definidas em arquivos Smithy dentro do projeto de modelo. A definição principal do serviço está em 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,
// Adicione suas operações aqui
]
errors: [
ValidationException
]
}

Operações individuais são definidas em arquivos separados no diretório 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
}

As implementações das operações estão localizadas no diretório src/operations/ do projeto backend. Cada operação é implementada usando os tipos gerados do TypeScript Server SDK (gerados em tempo de build a partir do seu modelo Smithy).

import { ServiceContext } from '../context.js';
import { Echo as EchoOperation } from '../generated/ssdk/index.js';
export const Echo: EchoOperation<ServiceContext> = async (input) => {
// Sua lógica de negócios aqui
return {
message: `Echo: ${input.message}` // tipagem segura baseada no seu modelo Smithy
};
};

As operações devem ser registradas na definição do serviço em src/service.ts:

import { ServiceContext } from './context.js';
import { YourServiceService } from './generated/ssdk/index.js';
import { Echo } from './operations/echo.js';
// Importe outras operações aqui
// Registre as operações no serviço aqui
export const Service: YourServiceService<ServiceContext> = {
Echo,
// Adicione outras operações aqui
};

Você pode definir contexto compartilhado para suas operações em context.ts:

export interface ServiceContext {
// Tracer, logger e metrics do Powertools são fornecidos por padrão
tracer: Tracer;
logger: Logger;
metrics: Metrics;
// Adicione dependências compartilhadas, conexões de banco de dados, etc.
dbClient: any;
userIdentity: string;
}

Este contexto é passado para todas as implementações de operações e pode ser usado para compartilhar recursos como conexões de banco de dados, configuração ou utilitários de logging.

O gerador configura logging estruturado usando AWS Lambda Powertools com injeção automática de contexto via middleware Middy.

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

Você pode referenciar o logger das suas implementações de operações via contexto:

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('Sua mensagem de log');
// ...
};

O rastreamento com AWS X-Ray é configurado automaticamente via middleware captureLambdaHandler.

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

Você pode adicionar subsegmentos personalizados aos seus traces nas operações:

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) => {
// Cria um novo subsegmento
const subsegment = ctx.tracer.getSegment()?.addNewSubsegment('operacao-personalizada');
try {
// Sua lógica aqui
} catch (error) {
subsegment?.addError(error as Error);
throw error;
} finally {
subsegment?.close();
}
};

Métricas do CloudWatch são coletadas automaticamente para cada requisição via middleware logMetrics.

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

Você pode adicionar métricas personalizadas nas suas operações:

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("MetricaPersonalizada", MetricUnit.Count, 1);
// ...
};

Smithy fornece tratamento de erros embutido. Você pode definir erros personalizados no seu modelo Smithy:

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

E registrá-los na sua operação/serviço:

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

Então lançá-los na implementação TypeScript:

import { InvalidRequestError } from '../generated/ssdk/index.js';
export const MyOperation: MyOperationHandler<ServiceContext> = async (input) => {
if (!input.requiredField) {
throw new InvalidRequestError({
message: "Campo obrigatório faltando"
});
}
return { /* resposta de sucesso */ };
};

Quando sua API é protegida por autenticação, suas operações frequentemente precisam saber quem está chamando. A abordagem recomendada é resolver a identidade do chamador uma vez no handler e passá-la através do contexto do serviço para consumo por operações específicas.

Vamos modelar o caso não autorizado como um erro Smithy para que ele serialize para uma resposta 403 adequada. Adicione-o ao seu modelo, por exemplo em model/src/operations/errors.smithy, e referencie-o em qualquer operação que requer identidade:

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

Primeiro, exponha a identidade resolvida no contexto do serviço em src/context.ts. Fornecemos isso como uma função para que o UnauthorizedError seja lançado de dentro de uma operação (onde o Server SDK o serializa para um 403), em vez de a partir do handler:

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>;
}

Em seguida, escreva o resolver em src/identity.ts. Ele lança UnauthorizedError quando o chamador não pode ser determinado. A implementação depende do método auth selecionado:

auth = iam

Para autenticação IAM, procuramos o chamador no Cognito usando o sub extraído do evento do 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

Com auth: 'cognito', o autorizador Cognito User Pools do API Gateway verifica o JWT que o chamador fornece no cabeçalho Authorization e coloca as claims verificadas no evento em 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 };
};

Em seguida, conecte o resolver ao contexto em 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),
});

Agora podemos usar a identidade resolvida em uma operação, por exemplo em 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}` };
};

O projeto de modelo Smithy usa Docker para construir os artefatos Smithy e gerar o TypeScript Server SDK:

Terminal window
pnpm nx build <model-project>

Este processo:

  1. Compila o modelo Smithy e o valida
  2. Gera especificação OpenAPI a partir do modelo Smithy
  3. Cria TypeScript Server SDK com interfaces de operação tipadas
  4. Gera artefatos de build em dist/<model-project>/build/

O projeto backend copia automaticamente o SDK gerado durante a compilação:

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

O gerador configura automaticamente um destino bundle que utiliza o Rolldown para criar um pacote de implantação:

Terminal window
pnpm nx bundle <project-name>

A configuração do Rolldown pode ser encontrada em rolldown.config.ts, com uma entrada para cada pacote a ser gerado. O Rolldown gerencia a criação de múltiplos pacotes em paralelo, se definidos.

O gerador configura um servidor de desenvolvimento local com hot reloading:

Terminal window
pnpm nx serve <backend-project>

O gerador cria infraestrutura CDK ou Terraform baseada no iacProvider selecionado.

O construct CDK para implantar sua API está na pasta common/constructs:

import { MyApi } from ':my-scope/common-constructs';
export class ExampleStack extends Stack {
constructor(scope: Construct, id: string) {
// Adicione a API à sua stack
const api = new MyApi(this, 'MyApi', {
integrations: MyApi.defaultIntegrations(this).build(),
});
}
}

Isso configura:

  1. Uma função AWS Lambda para o serviço Smithy
  2. API Gateway REST API como trigger da função
  3. Roles e permissões IAM
  4. Grupo de logs CloudWatch
  5. Configuração de rastreamento X-Ray
auth = cognito

Para APIs REST, o construto gerado associa um Web ACL do AWS WAFv2 ao estágio do API Gateway por padrão. O Web ACL usa o conjunto de regras padrão gerenciado pela AWS (AWSManagedRulesCommonRuleSet e AWSManagedRulesKnownBadInputsRuleSet), fornecendo proteção contra explorações web comuns, incluindo o OWASP Top 10. Os logs de requisições do WAF são gravados em um grupo do CloudWatch Logs.

Você pode editar o construto rest-api gerado para adicionar, remover ou ajustar regras (por exemplo, para adicionar regras baseadas em taxa ou grupos de regras gerenciadas adicionais).

Para desativar (por exemplo, para anexar seu próprio Web ACL), defina enableWaf como false:

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

Para APIs REST, a infraestrutura gerada habilita o registro de acesso por padrão, escrevendo uma linha JSON estruturada por solicitação em um grupo dedicado do CloudWatch Logs. O grupo de logs é criptografado com uma chave KMS gerenciada pelo cliente e retido por um ano.

O API Gateway escreve logs de acesso usando uma função do CloudWatch Logs no nível da conta. Essa função é configurada na configuração AWS::ApiGateway::Account, que é um singleton por região por conta — há apenas uma função para cada API REST na região. Para gerenciar isso com segurança em várias pilhas implantadas independentemente, a infraestrutura gerada:

  • Cria uma função compartilhada do CloudWatch Logs e a configura na conta somente quando nenhuma função funcional já está definida, para que as implantações nunca substituam uma função que outra pilha possui.
  • Deixa a configuração da conta intocada na desmontagem, para que a destruição de uma pilha nunca desabilite o registro para outras APIs REST na região.

A função da conta é gerenciada pelo construto ApiGatewayAccount, um singleton com escopo de pilha resolvido via ApiGatewayAccount.ensure(scope). O estágio de cada API REST depende dele, e a função é configurada por um recurso personalizado baseado em Lambda.

Você pode personalizar o formato do log de acesso passando deployOptions ao construir sua API:

const api = new MyApi(this, 'MyApi', {
integrations: MyApi.defaultIntegrations(this).build(),
deployOptions: {
accessLogFormat: AccessLogFormat.clf(),
},
});

Os construtos CDK da API REST/HTTP são configurados para fornecer uma interface type-safe para definir integrações para cada uma de suas operações.

Os construtos CDK fornecem suporte completo a integrações type-safe conforme descrito abaixo.

Você pode usar o método estático defaultIntegrations para utilizar o padrão padrão, que define uma função AWS Lambda individual para cada operação:

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

Você pode acessar as funções AWS Lambda subjacentes através da propriedade integrations do construto da API, de forma type-safe. Por exemplo, se sua API define uma operação chamada sayHello e você precisa adicionar permissões a esta função, você pode fazer isso da seguinte forma:

const api = new MyApi(this, 'MyApi', {
integrations: MyApi.defaultIntegrations(this).build(),
});
// sayHello é tipado conforme as operações definidas em sua API
api.integrations.sayHello.handler.addToRolePolicy(new PolicyStatement({
effect: Effect.ALLOW,
actions: [...],
resources: [...],
}));

Se sua API usa o padrão shared, o roteador Lambda compartilhado é exposto como api.integrations.$router:

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

Se você deseja personalizar as opções usadas ao criar a função Lambda para cada integração padrão, pode usar o método withDefaultOptions. Por exemplo, se deseja que todas suas funções Lambda residam em uma VPC:

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

Para personalizar as opções usadas para criar a integração padrão para operações específicas (sem afetar as outras), você pode usar o método withOperationOptions. Por exemplo, se você deseja aumentar o timeout da função Lambda para apenas uma operação:

const api = new MyApi(this, 'MyApi', {
integrations: MyApi.defaultIntegrations(this)
.withOperationOptions({
sayHello: {
timeout: Duration.seconds(60),
},
})
.build(),
});
// As operações selecionadas permanecem integrações padrão, então ainda são tipadas adequadamente:
api.integrations.sayHello.handler.addToRolePolicy(new PolicyStatement({ ... }));

As opções que você especifica são mescladas com as opções de integração padrão (e quaisquer opções definidas via withDefaultOptions). Note que você não pode especificar opções para operações que você substituiu via withOverrides, pois estas não usam mais a integração padrão.

Você encontrará um erro de tipo se a mesma operação for alvo tanto de withOperationOptions quanto de withOverrides, independentemente da ordem em que você os chamar.

Você também pode sobrescrever integrações para operações específicas usando o método withOverrides. Cada sobrescrita deve especificar uma propriedade integration que é tipada ao construto de integração CDK apropriado para a API HTTP ou REST. O método withOverrides também é type-safe. Por exemplo, se você deseja sobrescrever uma API getDocumentation para apontar para documentação hospedada em um site externo:

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

Você notará que a integração sobrescrita não terá mais uma propriedade handler quando acessada via api.integrations.getDocumentation.

Você pode adicionar propriedades adicionais a uma integração que também serão tipadas adequadamente, permitindo que outros tipos de integração sejam abstraídos mas permaneçam type-safe. Por exemplo, se você criou uma integração S3 para uma API REST e depois deseja referenciar o bucket para uma operação específica:

const storageBucket = new Bucket(this, 'Bucket', { ... });
const apiGatewayRole = new Role(this, 'ApiGatewayS3Role', {
assumedBy: new ServicePrincipal('apigateway.amazonaws.com'),
});
storageBucket.grantRead(apiGatewayRole);
const api = new MyApi(this, 'MyApi', {
integrations: MyApi.defaultIntegrations(this)
.withOverrides({
getFile: {
bucket: storageBucket,
integration: new AwsIntegration({
service: 's3',
integrationHttpMethod: 'GET',
path: `${storageBucket.bucketName}/{fileName}`,
options: {
credentialsRole: apiGatewayRole,
requestParameters: {
'integration.request.path.fileName': 'method.request.querystring.fileName',
},
integrationResponses: [{ statusCode: '200' }],
},
}),
options: {
requestParameters: {
'method.request.querystring.fileName': true,
},
methodResponses: [{
statusCode: '200',
}],
}
},
})
.build(),
});
// Posteriormente, talvez em outro arquivo, você pode acessar a propriedade bucket que definimos
// de forma type-safe
api.integrations.getFile.bucket.grantRead(...);

Você também pode fornecer options em sua integração para sobrescrever opções específicas de método como autorizadores. Por exemplo, se desejar usar autenticação Cognito para sua operação getDocumentation:

new MyApi(this, 'MyApi', {
integrations: MyApi.defaultIntegrations(this)
.withOverrides({
getDocumentation: {
integration: new HttpIntegration('https://example.com/documentation'),
options: {
authorizer: new CognitoUserPoolsAuthorizer(...) // para REST, ou HttpUserPoolAuthorizer para HTTP API
}
},
})
.build(),
});

Se preferir, você pode optar por não usar as integrações padrão e fornecer diretamente uma para cada operação. Isso é útil se, por exemplo, cada operação precisar usar um tipo diferente de integração ou se você quiser receber um erro de tipo ao adicionar novas operações:

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

Os construtos CDK de API gerados suportam dois padrões de integração:

  • isolated cria uma função Lambda por operação. Este é o padrão para APIs geradas.
  • shared cria um único roteador Lambda padrão e o reutiliza para cada operação, a menos que você sobrescreva integrações específicas.

isolated oferece permissões e configuração mais granulares por operação. shared reduz a proliferação de funções Lambda e integrações do API Gateway, ainda permitindo sobrescritas seletivas.

Por exemplo, definir pattern como 'shared' cria uma única função em vez de uma por integração:

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

Como as operações são definidas em Smithy, usamos geração de código para fornecer metadados ao construct CDK para integrações tipadas.

Um target generate:<ApiName>-metadata é adicionado ao project.json dos constructs comuns para facilitar esta geração de código, que emite um arquivo como packages/common/constructs/src/generated/my-api/metadata.gen.ts. Como isso é gerado em tempo de build, é ignorado no controle de versão.

auth = iam

Se você selecionou autenticação IAM, pode usar o método grantInvokeAccess para conceder acesso à sua API:

api.grantInvokeAccess(myIdentityPool.authenticatedRole);

Para invocar sua API de um website React, você pode usar o gerador connection, que fornece geração de cliente tipado a partir do seu modelo Smithy.

Use o gerador connection para integrar este projeto com outros em seu workspace. As seguintes conexões envolvem este projeto:

Smithy
React para API SmithyChame uma API Smithy de um site React
SmithyAmazon Aurora
API Smithy para Banco de Dados RelacionalConecte uma API Smithy a um banco de dados relacional Aurora
SmithyAmazon DynamoDB
API Smithy para TypeScript DynamoDBConecte uma API Smithy a uma tabela DynamoDB