@aws/nx-plugin-mcp 1.0.0-rc.95 → 1.0.0-rc.97
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/aws-nx-mcp.js +61 -14
- package/docs/get_started/existing-project.mdx +6 -3
- package/docs/get_started/quick-start.mdx +8 -0
- package/docs/get_started/tutorials/dungeon-game/1.mdx +12 -14
- package/docs/get_started/tutorials/dungeon-game/2.mdx +10 -2
- package/docs/get_started/tutorials/dungeon-game/3.mdx +4 -0
- package/docs/get_started/tutorials/dungeon-game/4.mdx +2 -2
- package/docs/guides/agentcore-gateway.mdx +4 -2
- package/docs/guides/agentcore-harness.mdx +2 -1
- package/docs/guides/astro-docs.mdx +25 -7
- package/docs/guides/connection/py-agent-a2a.mdx +2 -0
- package/docs/guides/connection/py-agent-dynamodb.mdx +1 -1
- package/docs/guides/connection/py-agent-gateway.mdx +3 -0
- package/docs/guides/connection/py-agent-mcp.mdx +18 -4
- package/docs/guides/connection/py-agent-rdb.mdx +5 -4
- package/docs/guides/connection/py-fast-api-dynamodb.mdx +1 -1
- package/docs/guides/connection/py-fast-api-rdb.mdx +7 -2
- package/docs/guides/connection/py-mcp-server-dynamodb.mdx +1 -1
- package/docs/guides/connection/py-mcp-server-rdb.mdx +6 -6
- package/docs/guides/connection/react-agui.mdx +34 -25
- package/docs/guides/connection/react-fastapi.mdx +114 -116
- package/docs/guides/connection/react-py-agent.mdx +4 -0
- package/docs/guides/connection/react-smithy.mdx +152 -98
- package/docs/guides/connection/react-trpc.mdx +13 -6
- package/docs/guides/connection/smithy-dynamodb.mdx +2 -8
- package/docs/guides/connection/smithy-rdb.mdx +3 -6
- package/docs/guides/connection/trpc-rdb.mdx +6 -6
- package/docs/guides/connection/ts-agent-a2a.mdx +4 -4
- package/docs/guides/connection/ts-agent-dynamodb.mdx +13 -11
- package/docs/guides/connection/ts-agent-gateway.mdx +2 -0
- package/docs/guides/connection/ts-agent-mcp.mdx +19 -7
- package/docs/guides/connection/ts-agent-rdb.mdx +2 -2
- package/docs/guides/connection/ts-mcp-server-dynamodb.mdx +11 -7
- package/docs/guides/connection/ts-mcp-server-rdb.mdx +4 -3
- package/docs/guides/docker-bundling.mdx +23 -3
- package/docs/guides/fastapi.mdx +16 -5
- package/docs/guides/py-agent.mdx +129 -54
- package/docs/guides/py-mcp-server.mdx +3 -1
- package/docs/guides/py-rdb.mdx +13 -4
- package/docs/guides/python-lambda-function.mdx +8 -8
- package/docs/guides/python-project.mdx +28 -25
- package/docs/guides/react-website-auth.mdx +8 -8
- package/docs/guides/react-website.mdx +46 -27
- package/docs/guides/runtime-config.mdx +24 -4
- package/docs/guides/security.mdx +1 -1
- package/docs/guides/terraform-project.mdx +8 -2
- package/docs/guides/trpc.mdx +96 -12
- package/docs/guides/ts-agent.mdx +17 -3
- package/docs/guides/ts-dcr-proxy.mdx +24 -6
- package/docs/guides/ts-lambda-function.mdx +7 -1
- package/docs/guides/ts-mcp-server.mdx +45 -15
- package/docs/guides/ts-rdb.mdx +9 -2
- package/docs/guides/ts-smithy-api.mdx +76 -7
- package/docs/guides/typescript-infrastructure.mdx +24 -10
- package/docs/guides/typescript-project.mdx +12 -5
- package/docs/guides/workspace.mdx +21 -9
- package/docs/snippets/api/type-safe-api-integrations.mdx +2 -0
- package/docs/snippets/connection/a2a-infrastructure.mdx +3 -0
- package/docs/snippets/connection/infra-project-prerequisite.mdx +8 -0
- package/docs/snippets/connection/strands-agent-rdb-ssl-requirements.mdx +1 -1
- package/docs/snippets/connection/ts-lambda-rdb-ssl-requirements.mdx +1 -1
- package/docs/snippets/connection/ts-mcp-server-rdb-ssl-requirements.mdx +1 -1
- package/docs/snippets/required-prerequisites.mdx +1 -1
- package/package.json +1 -1
- package/src/init/schema.json +5 -0
- package/src/py/project/schema.json +3 -1
|
@@ -43,16 +43,10 @@ Import entity factories from the DynamoDB package and use them inside your opera
|
|
|
43
43
|
|
|
44
44
|
```ts title="packages/api/src/operations/list-examples.ts"
|
|
45
45
|
import { createExampleEntity } from '@my-scope/my-table';
|
|
46
|
-
import {
|
|
47
|
-
ListExamplesOperationInput,
|
|
48
|
-
ListExamplesOperationOutput,
|
|
49
|
-
} from '../generated/ssdk/index.js';
|
|
50
46
|
import { ServiceContext } from '../context.js';
|
|
47
|
+
import { ListExamples as ListExamplesOperation } from '../generated/ssdk/index.js';
|
|
51
48
|
|
|
52
|
-
export const
|
|
53
|
-
_input: ListExamplesOperationInput,
|
|
54
|
-
_ctx: ServiceContext,
|
|
55
|
-
): Promise<ListExamplesOperationOutput> => {
|
|
49
|
+
export const ListExamples: ListExamplesOperation<ServiceContext> = async () => {
|
|
56
50
|
const entity = await createExampleEntity();
|
|
57
51
|
const result = await entity.scan.go();
|
|
58
52
|
return { items: result.data };
|
|
@@ -89,14 +89,11 @@ export const lambdaHandler = async (event: APIGatewayProxyEvent) => {
|
|
|
89
89
|
|
|
90
90
|
Access `db` from the context in your operation implementations:
|
|
91
91
|
|
|
92
|
-
```ts title="packages/api/src/operations/list-users.ts" {
|
|
93
|
-
import { ListUsersOperationInput, ListUsersOperationOutput } from '../generated/ssdk/index.js';
|
|
92
|
+
```ts title="packages/api/src/operations/list-users.ts" {5}
|
|
94
93
|
import { ServiceContext } from '../context.js';
|
|
94
|
+
import { ListUsers as ListUsersOperation } from '../generated/ssdk/index.js';
|
|
95
95
|
|
|
96
|
-
export const
|
|
97
|
-
input: ListUsersOperationInput,
|
|
98
|
-
ctx: ServiceContext,
|
|
99
|
-
): Promise<ListUsersOperationOutput> => {
|
|
96
|
+
export const ListUsers: ListUsersOperation<ServiceContext> = async (input, ctx) => {
|
|
100
97
|
const users = await ctx.myDb.user.findMany();
|
|
101
98
|
return { users };
|
|
102
99
|
};
|
|
@@ -35,13 +35,14 @@ Select your tRPC API project as the source and your relational database project
|
|
|
35
35
|
|
|
36
36
|
## Generator Output
|
|
37
37
|
|
|
38
|
-
The generator creates a middleware file in your tRPC API project:
|
|
38
|
+
The generator creates a middleware file in your tRPC API project, and adds it to the tRPC context:
|
|
39
39
|
|
|
40
40
|
<FileTree>
|
|
41
41
|
|
|
42
42
|
- packages/api/src
|
|
43
43
|
- middleware
|
|
44
44
|
- \<db-name>.ts tRPC plugin exposing the Prisma client in procedure context
|
|
45
|
+
- init.ts The database's context interface added to `Context`
|
|
45
46
|
|
|
46
47
|
</FileTree>
|
|
47
48
|
|
|
@@ -53,12 +54,11 @@ Additionally, it updates your tRPC API's `dev` target to start the database auto
|
|
|
53
54
|
|
|
54
55
|
Add the generated plugin to your tRPC router so all procedures using it gain access to the database:
|
|
55
56
|
|
|
56
|
-
```ts title="packages/api/src/router.ts" {2,
|
|
57
|
+
```ts title="packages/api/src/router.ts" {2,4}
|
|
57
58
|
import { t } from './init.js';
|
|
58
59
|
import { createMyDbPlugin } from './middleware/my-db.js';
|
|
59
60
|
|
|
60
|
-
export const
|
|
61
|
-
.concat(createMyDbPlugin());
|
|
61
|
+
export const dbProcedure = t.procedure.concat(createMyDbPlugin());
|
|
62
62
|
```
|
|
63
63
|
|
|
64
64
|
### Access the Database in Procedures
|
|
@@ -67,9 +67,9 @@ The plugin merges `IMyDbContext` into your procedure context, making `myDb` avai
|
|
|
67
67
|
|
|
68
68
|
```ts title="packages/api/src/procedures/users.ts" {7-8}
|
|
69
69
|
import { z } from 'zod';
|
|
70
|
-
import {
|
|
70
|
+
import { dbProcedure } from '../router.js';
|
|
71
71
|
|
|
72
|
-
export const listUsers =
|
|
72
|
+
export const listUsers = dbProcedure
|
|
73
73
|
.output(z.array(z.object({ id: z.string(), name: z.string() })))
|
|
74
74
|
.query(async ({ ctx }) => {
|
|
75
75
|
// ctx.myDb is the Prisma client — typed as Awaited<ReturnType<typeof getPrisma>>
|
|
@@ -73,8 +73,8 @@ import { Agent, tool } from '@strands-agents/sdk';
|
|
|
73
73
|
import { RemoteAgentClientStrands } from '@my-scope/agent-connection';
|
|
74
74
|
import { z } from 'zod';
|
|
75
75
|
|
|
76
|
-
export const getAgent = async (
|
|
77
|
-
const remoteAgent = await RemoteAgentClientStrands.create(
|
|
76
|
+
export const getAgent = async () => {
|
|
77
|
+
const remoteAgent = await RemoteAgentClientStrands.create();
|
|
78
78
|
const remoteAgentTool = tool({
|
|
79
79
|
name: 'askRemoteAgent',
|
|
80
80
|
description: 'Delegate a question to the remote RemoteAgent A2A agent and return its reply.',
|
|
@@ -88,9 +88,9 @@ export const getAgent = async (sessionId: string) => {
|
|
|
88
88
|
};
|
|
89
89
|
```
|
|
90
90
|
|
|
91
|
-
The `
|
|
91
|
+
The AgentCore session ID is propagated to the remote agent automatically via the `X-Amzn-Bedrock-AgentCore-Runtime-Session-Id` header, so `create()` takes no arguments: the agent server binds the inbound request's session into an [`AsyncLocalStorage`](https://nodejs.org/api/async_context.html#class-asynclocalstorage) context (`enterSessionContext` in the generated `router.ts`, or `runWithSessionId` in the A2A/AG-UI session middleware), and the connection client's fetch in `agentcore-fetch.ts` stamps it on every outbound call — ensuring consistency for [Bedrock AgentCore Observability](https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/observability.html).
|
|
92
92
|
|
|
93
|
-
Under the hood, `RemoteAgentClientStrands.create(
|
|
93
|
+
Under the hood, `RemoteAgentClientStrands.create()` returns a Strands `A2AAgent` configured with a SigV4-signing `clientFactory` when deployed to AWS, and a plain `http://localhost:<port>/` endpoint when `LOCAL_DEV=true`. The signing and endpoint resolution live in the framework-agnostic `agentcore-a2a-client-config.ts`; only the thin `agentcore-a2a-client-strands.ts` depends on Strands.
|
|
94
94
|
|
|
95
95
|
## Infrastructure
|
|
96
96
|
|
|
@@ -43,21 +43,23 @@ Import entity factories from the DynamoDB package and use them inside your agent
|
|
|
43
43
|
|
|
44
44
|
```ts title="packages/my-service/src/my-agent/agent.ts"
|
|
45
45
|
import { createExampleEntity } from '@my-scope/my-table';
|
|
46
|
+
import { z } from 'zod';
|
|
47
|
+
|
|
48
|
+
const listExamples = tool({
|
|
49
|
+
name: 'list_examples',
|
|
50
|
+
description: 'List all example items',
|
|
51
|
+
inputSchema: z.object({}),
|
|
52
|
+
callback: async () => {
|
|
53
|
+
const entity = await createExampleEntity();
|
|
54
|
+
const result = await entity.scan.go();
|
|
55
|
+
return result.data;
|
|
56
|
+
},
|
|
57
|
+
});
|
|
46
58
|
|
|
47
59
|
export const getAgent = async () => {
|
|
48
60
|
// ...
|
|
49
61
|
return new Agent({
|
|
50
|
-
tools: [
|
|
51
|
-
tool({
|
|
52
|
-
name: 'list_examples',
|
|
53
|
-
description: 'List all example items',
|
|
54
|
-
func: async () => {
|
|
55
|
-
const entity = await createExampleEntity();
|
|
56
|
-
const result = await entity.scan.go();
|
|
57
|
-
return result.data;
|
|
58
|
-
},
|
|
59
|
-
}),
|
|
60
|
-
],
|
|
62
|
+
tools: [listExamples],
|
|
61
63
|
});
|
|
62
64
|
};
|
|
63
65
|
```
|
|
@@ -51,6 +51,8 @@ The generator emits shared core client files into your `agent-connection` packag
|
|
|
51
51
|
- src
|
|
52
52
|
- core/
|
|
53
53
|
- agentcore-endpoints.ts Framework-agnostic ARN/URL resolution
|
|
54
|
+
- agentcore-fetch.ts Framework-agnostic SigV4 / JWT / session-forwarding fetch
|
|
55
|
+
- agentcore-transport.ts Shared AgentCore transport plumbing
|
|
54
56
|
- agentcore-gateway-mcp-transport.ts Framework-agnostic Gateway MCP transport
|
|
55
57
|
- agentcore-gateway-mcp-client-strands.ts Strands MCP client for the deployed Gateway
|
|
56
58
|
- app/
|
|
@@ -13,6 +13,7 @@ import RunGenerator from '@components/run-generator.astro';
|
|
|
13
13
|
import GeneratorParameters from '@components/generator-parameters.astro';
|
|
14
14
|
import NxCommands from '@components/nx-commands.astro';
|
|
15
15
|
import Infrastructure from '@components/infrastructure.astro';
|
|
16
|
+
import Snippet from '@components/snippet.astro';
|
|
16
17
|
|
|
17
18
|
The `connection` generator can connect your <Link path="guides/ts-agent">TypeScript Agent</Link> to an MCP server (either <Link path="guides/ts-mcp-server">TypeScript</Link> or <Link path="guides/py-mcp-server">Python</Link>).
|
|
18
19
|
|
|
@@ -51,6 +52,7 @@ The generator creates a shared `agent-connection` package and modifies your agen
|
|
|
51
52
|
- core
|
|
52
53
|
- agentcore-endpoints.ts Framework-agnostic ARN/URL resolution
|
|
53
54
|
- agentcore-fetch.ts Framework-agnostic SigV4 / JWT / session-forwarding fetch
|
|
55
|
+
- agentcore-transport.ts Shared AgentCore transport plumbing
|
|
54
56
|
- agentcore-mcp-transport.ts Framework-agnostic MCP transport
|
|
55
57
|
- agentcore-mcp-client-strands.ts Strands MCP client wrapping the transport
|
|
56
58
|
- index.ts Exports all clients
|
|
@@ -72,8 +74,8 @@ The generator transforms your agent's `agent.ts` to use the MCP server's tools:
|
|
|
72
74
|
import { Agent, tool } from '@strands-agents/sdk';
|
|
73
75
|
import { MyMcpServerClientStrands } from '@my-scope/agent-connection';
|
|
74
76
|
|
|
75
|
-
export const getAgent = async (
|
|
76
|
-
const myMcpServerClient = await MyMcpServerClientStrands.create(
|
|
77
|
+
export const getAgent = async () => {
|
|
78
|
+
const myMcpServerClient = await MyMcpServerClientStrands.create();
|
|
77
79
|
return new Agent({
|
|
78
80
|
systemPrompt: '...',
|
|
79
81
|
tools: [myMcpServerClient],
|
|
@@ -81,10 +83,12 @@ export const getAgent = async (sessionId: string) => {
|
|
|
81
83
|
};
|
|
82
84
|
```
|
|
83
85
|
|
|
84
|
-
The `
|
|
86
|
+
The AgentCore session ID is propagated to the MCP server automatically via the `X-Amzn-Bedrock-AgentCore-Runtime-Session-Id` header, so `create()` takes no arguments: the agent server binds the inbound request's session into an [`AsyncLocalStorage`](https://nodejs.org/api/async_context.html#class-asynclocalstorage) context (`enterSessionContext` in the generated `router.ts`, or `runWithSessionId` in the A2A/AG-UI session middleware), and the connection client's fetch in `agentcore-fetch.ts` stamps it on every outbound call — ensuring consistency for [Bedrock AgentCore Observability](https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/observability.html).
|
|
85
87
|
|
|
86
88
|
## Infrastructure
|
|
87
89
|
|
|
90
|
+
<Snippet name="connection/infra-project-prerequisite" />
|
|
91
|
+
|
|
88
92
|
<Infrastructure>
|
|
89
93
|
<Fragment slot="cdk">
|
|
90
94
|
After running the connection generator, you need to grant the agent permission to invoke the MCP server:
|
|
@@ -97,12 +101,14 @@ const myAgent = new MyAgent(this, 'MyAgent');
|
|
|
97
101
|
mcpServer.grantInvokeAccess(myAgent);
|
|
98
102
|
```
|
|
99
103
|
|
|
104
|
+
`grantInvokeAccess` wires up the AgentCore invoke actions (`InvokeAgentRuntime`, `InvokeAgentRuntimeForUser` and `InvokeAgentRuntimeWithWebSocketStream`) on the MCP server's runtime ARN.
|
|
105
|
+
|
|
100
106
|
The MCP server's AgentCore runtime ARN is automatically registered in the `agentcore` namespace of <Link path="guides/runtime-config">Runtime Configuration</Link> by the generated CDK construct, so the agent can discover it at runtime.
|
|
101
107
|
</Fragment>
|
|
102
108
|
<Fragment slot="terraform">
|
|
103
109
|
After running the connection generator, you need to grant the agent permission to invoke the MCP server in your Terraform configuration:
|
|
104
110
|
|
|
105
|
-
```hcl title="packages/infra/src/main.tf" {9-
|
|
111
|
+
```hcl title="packages/infra/src/main.tf" {9-31}
|
|
106
112
|
module "inventory_mcp_server" {
|
|
107
113
|
source = "../../common/terraform/src/app/mcp-servers/inventory-mcp"
|
|
108
114
|
}
|
|
@@ -117,9 +123,15 @@ resource "aws_iam_policy" "agent_invoke_mcp" {
|
|
|
117
123
|
policy = jsonencode({
|
|
118
124
|
Version = "2012-10-17"
|
|
119
125
|
Statement = [{
|
|
120
|
-
Effect
|
|
121
|
-
Action
|
|
122
|
-
|
|
126
|
+
Effect = "Allow"
|
|
127
|
+
Action = [
|
|
128
|
+
"bedrock-agentcore:InvokeAgentRuntime",
|
|
129
|
+
"bedrock-agentcore:InvokeAgentRuntimeWithWebSocketStream",
|
|
130
|
+
]
|
|
131
|
+
Resource = [
|
|
132
|
+
module.inventory_mcp_server.agent_core_runtime_arn,
|
|
133
|
+
"${module.inventory_mcp_server.agent_core_runtime_arn}/*",
|
|
134
|
+
]
|
|
123
135
|
}]
|
|
124
136
|
})
|
|
125
137
|
}
|
|
@@ -36,13 +36,13 @@ Select your Agent project as the source and your relational database project as
|
|
|
36
36
|
|
|
37
37
|
## Generator Output
|
|
38
38
|
|
|
39
|
-
The generator modifies
|
|
39
|
+
The generator modifies the following files in your agent's source directory:
|
|
40
40
|
|
|
41
41
|
<FileTree>
|
|
42
42
|
|
|
43
43
|
- packages/my-service/src/my-agent
|
|
44
44
|
- agent.ts Prisma client fetched inside `getAgent` and available to tools
|
|
45
|
-
- Dockerfile RDS CA bundle installed for SSL connections to Aurora
|
|
45
|
+
- Dockerfile RDS CA bundle installed for SSL connections to Aurora (only when the agent's `infra` is `agentcore-ecr`)
|
|
46
46
|
|
|
47
47
|
</FileTree>
|
|
48
48
|
|
|
@@ -47,13 +47,17 @@ import { createExampleEntity } from '@my-scope/my-table';
|
|
|
47
47
|
export const createServer = async () => {
|
|
48
48
|
const server = new McpServer({ name: 'my-service', version: '1.0.0' });
|
|
49
49
|
|
|
50
|
-
server.
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
50
|
+
server.registerTool(
|
|
51
|
+
'list_examples',
|
|
52
|
+
{ description: 'List all example items', inputSchema: {} },
|
|
53
|
+
async () => {
|
|
54
|
+
const entity = await createExampleEntity();
|
|
55
|
+
const result = await entity.scan.go();
|
|
56
|
+
return {
|
|
57
|
+
content: [{ type: 'text' as const, text: JSON.stringify(result.data) }],
|
|
58
|
+
};
|
|
59
|
+
},
|
|
60
|
+
);
|
|
57
61
|
|
|
58
62
|
return server;
|
|
59
63
|
};
|
|
@@ -36,13 +36,13 @@ Select your MCP server project as the source and your relational database projec
|
|
|
36
36
|
|
|
37
37
|
## Generator Output
|
|
38
38
|
|
|
39
|
-
The generator modifies
|
|
39
|
+
The generator modifies the following files in your MCP server's source directory:
|
|
40
40
|
|
|
41
41
|
<FileTree>
|
|
42
42
|
|
|
43
43
|
- packages/my-service/src/my-mcp
|
|
44
44
|
- server.ts Prisma client fetched and available to all tools registered inside `createServer`
|
|
45
|
-
- Dockerfile RDS CA bundle installed for SSL connections to Aurora
|
|
45
|
+
- Dockerfile RDS CA bundle installed for SSL connections to Aurora (only when the MCP server's `infra` is `agentcore-ecr`)
|
|
46
46
|
|
|
47
47
|
</FileTree>
|
|
48
48
|
|
|
@@ -52,11 +52,12 @@ Additionally, the `<mcp-server-name>-dev` target is updated to depend on the dat
|
|
|
52
52
|
|
|
53
53
|
The Prisma client is fetched inside `createServer` and available to all tools and resources registered there:
|
|
54
54
|
|
|
55
|
-
```ts title="packages/my-service/src/my-mcp/server.ts" {1,4}
|
|
55
|
+
```ts title="packages/my-service/src/my-mcp/server.ts" {1,4,5}
|
|
56
56
|
import { getPrisma as getMyDb } from '@my-scope/my-db';
|
|
57
57
|
|
|
58
58
|
export const createServer = async () => {
|
|
59
59
|
const myDb = await getMyDb();
|
|
60
|
+
myDb.$on('error', console.error);
|
|
60
61
|
const server = new McpServer({ name: 'my-service', version: '1.0.0' });
|
|
61
62
|
// register tools/resources that use myDb
|
|
62
63
|
return server;
|
|
@@ -133,11 +133,18 @@ Some npm packages cannot be bundled because they rely on dynamic `require`, nati
|
|
|
133
133
|
|
|
134
134
|
### Dockerfile
|
|
135
135
|
|
|
136
|
-
Create a `Dockerfile` in your project source directory. The file does nothing more than `COPY` the bundle into a Node base image, plus `npm install` any `external` packages that could not be bundled. Place the `RUN npm install` step **before** the `COPY`, so Docker can cache the installed `node_modules` layer and only re-run it when the dependency list actually changes:
|
|
136
|
+
Create a `Dockerfile` in your project source directory. The file does nothing more than [apply the base image's security updates](#apply-the-distributions-security-updates), `COPY` the bundle into a Node base image, plus `npm install` any `external` packages that could not be bundled. Place the `RUN npm install` step **before** the `COPY`, so Docker can cache the installed `node_modules` layer and only re-run it when the dependency list actually changes:
|
|
137
137
|
|
|
138
138
|
```dockerfile
|
|
139
139
|
FROM public.ecr.aws/docker/library/node:lts
|
|
140
140
|
|
|
141
|
+
# Apply the distribution's security updates: base image tags lag behind the
|
|
142
|
+
# security suite, so their OS packages carry vulnerabilities that already have a
|
|
143
|
+
# published fix.
|
|
144
|
+
RUN apt-get update && \
|
|
145
|
+
apt-get upgrade -y --no-install-recommends && \
|
|
146
|
+
rm -rf /var/lib/apt/lists/*
|
|
147
|
+
|
|
141
148
|
WORKDIR /app
|
|
142
149
|
|
|
143
150
|
# Install packages that cannot be bundled (declared as "external" in rolldown.config.ts).
|
|
@@ -236,11 +243,18 @@ Running `nx bundle my-project` produces `dist/packages/my-project/bundle-arm/` c
|
|
|
236
243
|
|
|
237
244
|
### Dockerfile
|
|
238
245
|
|
|
239
|
-
The `Dockerfile`
|
|
246
|
+
The `Dockerfile` [applies the base image's security updates](#apply-the-distributions-security-updates), then copies the bundle into a Python base image. Because `uv` already installed all dependencies into the bundle directory, you do not need to run `pip install` inside the image:
|
|
240
247
|
|
|
241
248
|
```dockerfile
|
|
242
249
|
FROM public.ecr.aws/docker/library/python:3.14-slim
|
|
243
250
|
|
|
251
|
+
# Apply the distribution's security updates: base image tags lag behind the
|
|
252
|
+
# security suite, so their OS packages carry vulnerabilities that already have a
|
|
253
|
+
# published fix.
|
|
254
|
+
RUN apt-get update && \
|
|
255
|
+
apt-get upgrade -y --no-install-recommends && \
|
|
256
|
+
rm -rf /var/lib/apt/lists/*
|
|
257
|
+
|
|
244
258
|
WORKDIR /app
|
|
245
259
|
|
|
246
260
|
# Copy bundled package (source + installed dependencies)
|
|
@@ -288,7 +302,13 @@ This clears the output directory, then copies both the bundle contents and the `
|
|
|
288
302
|
|
|
289
303
|
It's good practice to scan your images for known vulnerabilities. The generators that follow this pattern add a `trivy` target which scans the built image with [Trivy](https://trivy.dev/), running from the [ECR-hosted Trivy image](https://gallery.ecr.aws/aquasecurity/trivy), and exits non-zero on `HIGH` or `CRITICAL` findings.
|
|
290
304
|
|
|
291
|
-
|
|
305
|
+
The target copies a `.trivyignore` from the root of your project into the scan directory and passes it to Trivy with `--ignorefile`, so create that file first — an empty one is fine, and it is where you [suppress findings](#suppressing-findings) later:
|
|
306
|
+
|
|
307
|
+
```bash
|
|
308
|
+
touch packages/my-project/.trivyignore
|
|
309
|
+
```
|
|
310
|
+
|
|
311
|
+
Then add a `trivy` target which `dependsOn` your `docker` target. It saves the built image to a tarball and scans it via a workspace-relative bind mount, so the same command works under both `docker` and `finch`:
|
|
292
312
|
|
|
293
313
|
<Code lang="json" code={trivyTarget} />
|
|
294
314
|
|
package/docs/guides/fastapi.mdx
CHANGED
|
@@ -17,6 +17,7 @@ import PackageManagerShortCommand from '@components/package-manager-short-comman
|
|
|
17
17
|
import Infrastructure from '@components/infrastructure.astro';
|
|
18
18
|
import Snippet from '@components/snippet.astro';
|
|
19
19
|
import OptionFilter from '@components/option-filter.astro';
|
|
20
|
+
import { PY_VERSIONS } from '../../../../../../packages/nx-plugin/src/utils/versions';
|
|
20
21
|
|
|
21
22
|
[FastAPI](https://fastapi.tiangolo.com/) is a framework for building APIs in Python.
|
|
22
23
|
|
|
@@ -86,12 +87,12 @@ class Item(BaseModel):
|
|
|
86
87
|
@app.get("/items/{item_id}")
|
|
87
88
|
@tracer.capture_method
|
|
88
89
|
def get_item(item_id: int) -> Item:
|
|
89
|
-
return Item(name
|
|
90
|
+
return Item(name=f"Item {item_id}")
|
|
90
91
|
|
|
91
92
|
@app.post("/items")
|
|
92
93
|
@tracer.capture_method
|
|
93
|
-
def create_item(item: Item):
|
|
94
|
-
return
|
|
94
|
+
def create_item(item: Item) -> Item:
|
|
95
|
+
return item
|
|
95
96
|
```
|
|
96
97
|
|
|
97
98
|
The generator sets up several features automatically:
|
|
@@ -191,7 +192,11 @@ When your API is protected by authentication, your route handlers often need to
|
|
|
191
192
|
As an example, let's add a `/me` endpoint that returns details about the calling user. We'll implement the extraction as a [FastAPI dependency](https://fastapi.tiangolo.com/tutorial/dependencies/) so it can be reused across routes. The shape of the request context — and therefore how you extract the identity — depends on both your selected `auth` method and whether you deployed a REST or HTTP API.
|
|
192
193
|
|
|
193
194
|
<OptionFilter when={{ auth: 'iam' }} description="Identity extraction for IAM-authenticated APIs">
|
|
194
|
-
For `IAM` authentication, we look up the caller in Cognito using the sub extracted from the API Gateway request context
|
|
195
|
+
For `IAM` authentication, we look up the caller in Cognito using the sub extracted from the API Gateway request context, which requires `boto3`. Add it to your API project:
|
|
196
|
+
|
|
197
|
+
<NxCommands commands={[`run my-api:add boto3${PY_VERSIONS.boto3}`]} />
|
|
198
|
+
|
|
199
|
+
Create `identity.py` alongside `main.py`:
|
|
195
200
|
|
|
196
201
|
<Tabs syncKey="http-rest">
|
|
197
202
|
<TabItem label="REST API" _filter={{ infra: 'rest-lambda' }}>
|
|
@@ -652,6 +657,8 @@ If you are actively working on both your CDK infrastructure and FastAPI together
|
|
|
652
657
|
'run @<scope>/common-constructs:"generate:<ApiName>-metadata"',
|
|
653
658
|
]}
|
|
654
659
|
/>
|
|
660
|
+
|
|
661
|
+
`nx watch` requires the [Nx daemon](https://nx.dev/concepts/nx-daemon), which is enabled by default but switched off inside CI, Docker containers and sandboxed environments. Where it is off, the command exits with `Daemon is not running` — set `NX_DAEMON=true` to enable it.
|
|
655
662
|
:::
|
|
656
663
|
</Fragment>
|
|
657
664
|
<Fragment slot="terraform">
|
|
@@ -717,7 +724,7 @@ The key outputs from the API module that you can use for IAM policies are:
|
|
|
717
724
|
|
|
718
725
|
The generator configures a local development server that you can run with:
|
|
719
726
|
|
|
720
|
-
<NxCommands commands={['serve
|
|
727
|
+
<NxCommands commands={['serve <project-name>']} />
|
|
721
728
|
|
|
722
729
|
This starts a local FastAPI development server with:
|
|
723
730
|
|
|
@@ -725,6 +732,10 @@ This starts a local FastAPI development server with:
|
|
|
725
732
|
- Interactive API documentation at `/docs` or `/redoc`
|
|
726
733
|
- OpenAPI schema at `/openapi.json`
|
|
727
734
|
|
|
735
|
+
:::note[Project Names]
|
|
736
|
+
Python project names use underscores, so an API generated with `--name=MyApi` is addressed as `my_api` (or by its fully qualified name, `<scope>.my_api`). The port the server listens on is assigned per project — read it from the `serve` target in your project's `project.json`.
|
|
737
|
+
:::
|
|
738
|
+
|
|
728
739
|
## Invoking your FastAPI
|
|
729
740
|
|
|
730
741
|
To invoke your API from a React website, you can use the <Link path="guides/connection/react-fastapi">`connection` generator</Link>.
|