@aws/nx-plugin-mcp 0.119.0 → 0.121.0
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 +23 -35
- package/docs/guides/astro-docs.mdx +142 -0
- package/docs/guides/connection/py-agent-a2a.mdx +105 -0
- package/docs/guides/connection/py-agent-mcp.mdx +147 -0
- package/docs/guides/connection/react-agui.mdx +240 -0
- package/docs/guides/connection/react-fastapi.mdx +1476 -0
- package/docs/guides/connection/react-py-agent.mdx +198 -0
- package/docs/guides/connection/react-smithy.mdx +1355 -0
- package/docs/guides/connection/react-trpc.mdx +447 -0
- package/docs/guides/connection/react-ts-agent.mdx +198 -0
- package/docs/guides/connection/smithy-rdb.mdx +161 -0
- package/docs/guides/connection/trpc-rdb.mdx +127 -0
- package/docs/guides/connection/ts-agent-a2a.mdx +106 -0
- package/docs/guides/connection/ts-agent-mcp.mdx +144 -0
- package/docs/guides/connection/ts-agent-rdb.mdx +141 -0
- package/docs/guides/connection/ts-mcp-server-rdb.mdx +135 -0
- package/docs/guides/connection.mdx +126 -0
- package/docs/guides/docker-bundling.mdx +403 -0
- package/docs/guides/fastapi.mdx +509 -0
- package/docs/guides/license.mdx +293 -0
- package/docs/guides/nx-generator.mdx +588 -0
- package/docs/guides/py-agent.mdx +483 -0
- package/docs/guides/py-mcp-server.mdx +161 -0
- package/docs/guides/python-lambda-function.mdx +207 -0
- package/docs/guides/python-project.mdx +228 -0
- package/docs/guides/react-website-auth.mdx +216 -0
- package/docs/guides/react-website.mdx +421 -0
- package/docs/guides/runtime-config.mdx +312 -0
- package/docs/guides/terraform-project.mdx +317 -0
- package/docs/guides/trpc.mdx +816 -0
- package/docs/guides/ts-agent.mdx +436 -0
- package/docs/guides/ts-lambda-function.mdx +218 -0
- package/docs/guides/ts-mcp-server.mdx +169 -0
- package/docs/guides/ts-nx-plugin.mdx +159 -0
- package/docs/guides/ts-rdb.mdx +759 -0
- package/docs/guides/ts-smithy-api.mdx +661 -0
- package/docs/guides/typescript-infrastructure.mdx +408 -0
- package/docs/guides/typescript-project.mdx +312 -0
- package/docs/guides/workspace.mdx +181 -0
- package/docs/snippets/agent/architecture.mdx +72 -0
- package/docs/snippets/agent/bedrock-deployment.mdx +172 -0
- package/docs/snippets/agent/runtime-arn.mdx +64 -0
- package/docs/snippets/api/api-architecture.mdx +93 -0
- package/docs/snippets/api/api-choice-note.mdx +6 -0
- package/docs/snippets/api/cors-configuration-cdk-note.mdx +25 -0
- package/docs/snippets/api/cors-configuration-terraform-note.mdx +28 -0
- package/docs/snippets/api/shared-constructs.mdx +38 -0
- package/docs/snippets/api/type-safe-api-integrations.mdx +643 -0
- package/docs/snippets/api/waf-configuration.mdx +37 -0
- package/docs/snippets/connection/a2a-infrastructure.mdx +63 -0
- package/docs/snippets/connection/lambda-rdb-ssl-requirements.mdx +40 -0
- package/docs/snippets/connection/mcp-server-rdb-ssl-requirements.mdx +35 -0
- package/docs/snippets/connection/rdb-api-infrastructure.mdx +72 -0
- package/docs/snippets/connection/react-agent-infrastructure.mdx +61 -0
- package/docs/snippets/connection/strands-agent-rdb-ssl-requirements.mdx +35 -0
- package/docs/snippets/lambda-function/architecture.mdx +36 -0
- package/docs/snippets/lambda-function/deploying-your-function.mdx +118 -0
- package/docs/snippets/mcp/architecture.mdx +58 -0
- package/docs/snippets/mcp/assistant-docs.mdx +10 -0
- package/docs/snippets/mcp/bedrock-deployment.mdx +167 -0
- package/docs/snippets/mcp/config.mdx +13 -0
- package/docs/snippets/mcp/configuration-py.mdx +42 -0
- package/docs/snippets/mcp/configuration-ts.mdx +53 -0
- package/docs/snippets/mcp/observability.mdx +8 -0
- package/docs/snippets/mcp/shared-constructs.mdx +32 -0
- package/docs/snippets/pdk-migration/example/01-migrate-api.mdx +602 -0
- package/docs/snippets/pdk-migration/example/02-migrate-website.mdx +915 -0
- package/docs/snippets/pdk-migration/example/03-migrate-infra.mdx +161 -0
- package/docs/snippets/pdk-migration/example/04-deploy.mdx +229 -0
- package/docs/snippets/pdk-migration/faq/aws-arch.mdx +17 -0
- package/docs/snippets/pdk-migration/faq/cdk-graph.mdx +29 -0
- package/docs/snippets/pdk-migration/faq/infrastructure-python-java.mdx +19 -0
- package/docs/snippets/pdk-migration/faq/pdk-nag.mdx +15 -0
- package/docs/snippets/pdk-migration/faq/pipeline.mdx +15 -0
- package/docs/snippets/pdk-migration/faq/type-safe-api.mdx +310 -0
- package/docs/snippets/pdk-migration/faq/use-of-projen.mdx +15 -0
- package/docs/snippets/prerequisites.mdx +20 -0
- package/docs/snippets/required-prerequisites.mdx +12 -0
- package/docs/snippets/shared-constructs.mdx +40 -0
- package/docs/snippets/tools/acurl.mdx +73 -0
- package/docs/snippets/ts-bundle.mdx +14 -0
- package/package.json +1 -1
- package/src/py/agent/schema.json +1 -1
- package/src/py/fast-api/schema.json +2 -2
- package/src/py/mcp-server/schema.json +1 -1
- package/src/smithy/ts/api/schema.json +2 -2
- package/src/trpc/backend/schema.json +2 -2
- package/src/ts/agent/schema.json +1 -1
- package/src/ts/mcp-server/schema.json +1 -1
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: MCP Server to Relational Database
|
|
3
|
+
description: Connect a TypeScript MCP Server to a Relational Database
|
|
4
|
+
when:
|
|
5
|
+
sourceType: ts#mcp-server
|
|
6
|
+
targetType: ts#rdb
|
|
7
|
+
---
|
|
8
|
+
import { FileTree } from '@astrojs/starlight/components';
|
|
9
|
+
import Link from '@components/link.astro';
|
|
10
|
+
import RunGenerator from '@components/run-generator.astro';
|
|
11
|
+
import GeneratorParameters from '@components/generator-parameters.astro';
|
|
12
|
+
import NxCommands from '@components/nx-commands.astro';
|
|
13
|
+
import Infrastructure from '@components/infrastructure.astro';
|
|
14
|
+
import Snippet from '@components/snippet.astro';
|
|
15
|
+
|
|
16
|
+
The `connection` generator wires a <Link path="guides/ts-mcp-server">TypeScript MCP Server</Link> to a <Link path="guides/ts-rdb">Relational Database</Link> project, making a Prisma client available to all tools registered inside `createServer`.
|
|
17
|
+
|
|
18
|
+
## Prerequisites
|
|
19
|
+
|
|
20
|
+
Before using this generator, ensure you have:
|
|
21
|
+
|
|
22
|
+
1. A <Link path="guides/ts-mcp-server">`ts#mcp-server`</Link> project
|
|
23
|
+
2. A <Link path="guides/ts-rdb">`ts#rdb`</Link> project
|
|
24
|
+
|
|
25
|
+
## Usage
|
|
26
|
+
|
|
27
|
+
### Run the Generator
|
|
28
|
+
|
|
29
|
+
<RunGenerator generator="connection" />
|
|
30
|
+
|
|
31
|
+
Select your MCP server project as the source and your relational database project as the target. If the project contains multiple MCP server components, specify `sourceComponent` to disambiguate.
|
|
32
|
+
|
|
33
|
+
### Options
|
|
34
|
+
|
|
35
|
+
<GeneratorParameters generator="connection" />
|
|
36
|
+
|
|
37
|
+
## Generator Output
|
|
38
|
+
|
|
39
|
+
The generator modifies two files in your MCP server's source directory:
|
|
40
|
+
|
|
41
|
+
<FileTree>
|
|
42
|
+
|
|
43
|
+
- packages/my-service/src/my-mcp
|
|
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
|
|
46
|
+
|
|
47
|
+
</FileTree>
|
|
48
|
+
|
|
49
|
+
Additionally, the `<mcp-server-name>-serve-local` target is updated to depend on the database's `serve-local` target.
|
|
50
|
+
|
|
51
|
+
## How It Works
|
|
52
|
+
|
|
53
|
+
The Prisma client is fetched inside `createServer` and available to all tools and resources registered there:
|
|
54
|
+
|
|
55
|
+
```ts title="packages/my-service/src/my-mcp/server.ts" {1,4}
|
|
56
|
+
import { getPrisma as getMyDb } from ':my-scope/my-db';
|
|
57
|
+
|
|
58
|
+
export const createServer = async () => {
|
|
59
|
+
const myDb = await getMyDb();
|
|
60
|
+
const server = new McpServer({ name: 'my-service', version: '1.0.0' });
|
|
61
|
+
// register tools/resources that use myDb
|
|
62
|
+
return server;
|
|
63
|
+
};
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
## Multiple Databases
|
|
67
|
+
|
|
68
|
+
Running the generator again with a different target adds the second database alongside the first. Both are fetched inside `createServer`:
|
|
69
|
+
|
|
70
|
+
```ts title="packages/my-service/src/my-mcp/server.ts" {2,3}
|
|
71
|
+
export const createServer = async () => {
|
|
72
|
+
const postgresDb = await getPostgresDb();
|
|
73
|
+
const mysqlDb = await getMysqlDb();
|
|
74
|
+
const server = new McpServer({ ... });
|
|
75
|
+
// register tools using both clients
|
|
76
|
+
return server;
|
|
77
|
+
};
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
## Infrastructure
|
|
81
|
+
|
|
82
|
+
The generated MCP server construct implements `IGrantable` and `IConnectable`, so you can grant network and IAM access to the database directly on the construct.
|
|
83
|
+
|
|
84
|
+
<Infrastructure>
|
|
85
|
+
<Fragment slot="cdk">
|
|
86
|
+
|
|
87
|
+
```ts title="packages/infra/src/stacks/application-stack.ts"
|
|
88
|
+
import { MyDatabase } from ':my-scope/common-constructs';
|
|
89
|
+
|
|
90
|
+
const db = new MyDatabase(this, 'Db', { vpc, ... });
|
|
91
|
+
const myMcpServer = new MyMcpServer(this, 'MyMcpServer', { vpc, ... });
|
|
92
|
+
|
|
93
|
+
db.allowDefaultPortFrom(myMcpServer);
|
|
94
|
+
db.grantConnect(myMcpServer);
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
`allowDefaultPortFrom` opens the security group rule so the MCP server runtime can reach the database port. `grantConnect` grants IAM `rds-db:connect` permission to the server's execution role.
|
|
98
|
+
|
|
99
|
+
</Fragment>
|
|
100
|
+
<Fragment slot="terraform">
|
|
101
|
+
|
|
102
|
+
Pass the database module outputs into your MCP server module so it can reach the database and read its runtime configuration:
|
|
103
|
+
|
|
104
|
+
```hcl title="packages/infra/src/main.tf"
|
|
105
|
+
module "my_database" {
|
|
106
|
+
source = "../../common/terraform/src/app/dbs/my-database"
|
|
107
|
+
vpc_id = module.vpc.vpc_id
|
|
108
|
+
database_subnet_ids = module.vpc.private_isolated_subnet_ids
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
module "my_mcp_server" {
|
|
112
|
+
source = "../../common/terraform/src/app/mcp-servers/my-mcp-server"
|
|
113
|
+
|
|
114
|
+
appconfig_application_id = module.my_database.appconfig_application_id
|
|
115
|
+
database_cluster_resource_id = module.my_database.cluster_resource_id
|
|
116
|
+
database_runtime_user = module.my_database.database_runtime_user
|
|
117
|
+
database_security_group_id = module.my_database.security_group_id
|
|
118
|
+
database_port = module.my_database.cluster_port
|
|
119
|
+
}
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
Ensure the MCP server's execution role has `rds-db:connect` permission and that its security group can reach the database security group on the database port.
|
|
123
|
+
|
|
124
|
+
</Fragment>
|
|
125
|
+
</Infrastructure>
|
|
126
|
+
|
|
127
|
+
### SSL Requirements When Connecting Without RDS Proxy
|
|
128
|
+
|
|
129
|
+
<Snippet name="connection/mcp-server-rdb-ssl-requirements" parentHeading="SSL Requirements When Connecting Without RDS Proxy" />
|
|
130
|
+
|
|
131
|
+
## Local Development
|
|
132
|
+
|
|
133
|
+
<NxCommands commands={["<mcp-server-name>-serve-local <project-name>"]} />
|
|
134
|
+
|
|
135
|
+
This starts the MCP server and all connected databases. The `SERVE_LOCAL=true` environment variable causes each Prisma client to connect to its local Docker database instead of Aurora.
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Connection
|
|
3
|
+
description: Reference documentation for connections
|
|
4
|
+
---
|
|
5
|
+
import Astro from '@astrojs/react';
|
|
6
|
+
import { CardGrid, LinkButton } from '@astrojs/starlight/components';
|
|
7
|
+
import Link from '@components/link.astro';
|
|
8
|
+
import ConnectionCard from '@components/connection-card.astro';
|
|
9
|
+
|
|
10
|
+
This generator is used to connect projects together, such as websites calling APIs. Simply select the source project (for example the project that will call your API) and target project (for example your API project), and this generator will handle integrating the two.
|
|
11
|
+
|
|
12
|
+
### Supported Connections
|
|
13
|
+
|
|
14
|
+
The Connection generator supports the following connections:
|
|
15
|
+
|
|
16
|
+
<CardGrid>
|
|
17
|
+
<ConnectionCard
|
|
18
|
+
title="React to tRPC"
|
|
19
|
+
description="Call a tRPC API from a React website"
|
|
20
|
+
href={`/nx-plugin-for-aws/${Astro.currentLocale || 'en'}/guides/connection/react-trpc`}
|
|
21
|
+
source="react"
|
|
22
|
+
target="trpc"
|
|
23
|
+
/>
|
|
24
|
+
<ConnectionCard
|
|
25
|
+
title="React to FastAPI"
|
|
26
|
+
description="Call a Python FastAPI from a React website"
|
|
27
|
+
href={`/nx-plugin-for-aws/${Astro.currentLocale || 'en'}/guides/connection/react-fastapi`}
|
|
28
|
+
source="react"
|
|
29
|
+
target="fastapi"
|
|
30
|
+
/>
|
|
31
|
+
<ConnectionCard
|
|
32
|
+
title="React to Smithy API"
|
|
33
|
+
description="Call a Smithy API from a React website"
|
|
34
|
+
href={`/nx-plugin-for-aws/${Astro.currentLocale || 'en'}/guides/connection/react-smithy`}
|
|
35
|
+
source="react"
|
|
36
|
+
target="smithy"
|
|
37
|
+
/>
|
|
38
|
+
<ConnectionCard
|
|
39
|
+
title="React to Python Agent"
|
|
40
|
+
description="Call a Python Agent from a React website"
|
|
41
|
+
href={`/nx-plugin-for-aws/${Astro.currentLocale || 'en'}/guides/connection/react-py-agent`}
|
|
42
|
+
source="react"
|
|
43
|
+
target="strands"
|
|
44
|
+
targetBadge="python"
|
|
45
|
+
/>
|
|
46
|
+
<ConnectionCard
|
|
47
|
+
title="React to TypeScript Agent"
|
|
48
|
+
description="Call a TypeScript Agent from a React website"
|
|
49
|
+
href={`/nx-plugin-for-aws/${Astro.currentLocale || 'en'}/guides/connection/react-ts-agent`}
|
|
50
|
+
source="react"
|
|
51
|
+
target="strands"
|
|
52
|
+
targetBadge="typescript"
|
|
53
|
+
/>
|
|
54
|
+
<ConnectionCard
|
|
55
|
+
title="React to AG-UI Agent"
|
|
56
|
+
description="Call an Agent exposing the AG-UI protocol from a React website via CopilotKit"
|
|
57
|
+
href={`/nx-plugin-for-aws/${Astro.currentLocale || 'en'}/guides/connection/react-agui`}
|
|
58
|
+
source="react"
|
|
59
|
+
target="copilotkit"
|
|
60
|
+
/>
|
|
61
|
+
<ConnectionCard
|
|
62
|
+
title="Python Agent to MCP"
|
|
63
|
+
description="Connect a Python Agent to an MCP server"
|
|
64
|
+
href={`/nx-plugin-for-aws/${Astro.currentLocale || 'en'}/guides/connection/py-agent-mcp`}
|
|
65
|
+
source="strands"
|
|
66
|
+
sourceBadge="python"
|
|
67
|
+
target="mcp"
|
|
68
|
+
/>
|
|
69
|
+
<ConnectionCard
|
|
70
|
+
title="TypeScript Agent to MCP"
|
|
71
|
+
description="Connect a TypeScript Agent to an MCP server"
|
|
72
|
+
href={`/nx-plugin-for-aws/${Astro.currentLocale || 'en'}/guides/connection/ts-agent-mcp`}
|
|
73
|
+
source="strands"
|
|
74
|
+
sourceBadge="typescript"
|
|
75
|
+
target="mcp"
|
|
76
|
+
/>
|
|
77
|
+
<ConnectionCard
|
|
78
|
+
title="Python Agent to A2A Agent"
|
|
79
|
+
description="Connect a Python Agent to a remote A2A agent"
|
|
80
|
+
href={`/nx-plugin-for-aws/${Astro.currentLocale || 'en'}/guides/connection/py-agent-a2a`}
|
|
81
|
+
source="strands"
|
|
82
|
+
sourceBadge="python"
|
|
83
|
+
target="a2a"
|
|
84
|
+
/>
|
|
85
|
+
<ConnectionCard
|
|
86
|
+
title="TypeScript Agent to A2A Agent"
|
|
87
|
+
description="Connect a TypeScript Agent to a remote A2A agent"
|
|
88
|
+
href={`/nx-plugin-for-aws/${Astro.currentLocale || 'en'}/guides/connection/ts-agent-a2a`}
|
|
89
|
+
source="strands"
|
|
90
|
+
sourceBadge="typescript"
|
|
91
|
+
target="a2a"
|
|
92
|
+
/>
|
|
93
|
+
<ConnectionCard
|
|
94
|
+
title="tRPC API to Relational Database"
|
|
95
|
+
description="Connect a tRPC API to an Aurora relational database"
|
|
96
|
+
href={`/nx-plugin-for-aws/${Astro.currentLocale || 'en'}/guides/connection/trpc-rdb`}
|
|
97
|
+
source="trpc"
|
|
98
|
+
target="aurora"
|
|
99
|
+
/>
|
|
100
|
+
<ConnectionCard
|
|
101
|
+
title="Smithy API to Relational Database"
|
|
102
|
+
description="Connect a Smithy API to an Aurora relational database"
|
|
103
|
+
href={`/nx-plugin-for-aws/${Astro.currentLocale || 'en'}/guides/connection/smithy-rdb`}
|
|
104
|
+
source="smithy"
|
|
105
|
+
target="aurora"
|
|
106
|
+
/>
|
|
107
|
+
<ConnectionCard
|
|
108
|
+
title="TypeScript Agent to Relational Database"
|
|
109
|
+
description="Connect a TypeScript Agent to an Aurora relational database"
|
|
110
|
+
href={`/nx-plugin-for-aws/${Astro.currentLocale || 'en'}/guides/connection/ts-agent-rdb`}
|
|
111
|
+
source="strands"
|
|
112
|
+
sourceBadge="typescript"
|
|
113
|
+
target="aurora"
|
|
114
|
+
/>
|
|
115
|
+
<ConnectionCard
|
|
116
|
+
title="MCP Server to Relational Database"
|
|
117
|
+
description="Connect a TypeScript MCP Server to an Aurora relational database"
|
|
118
|
+
href={`/nx-plugin-for-aws/${Astro.currentLocale || 'en'}/guides/connection/ts-mcp-server-rdb`}
|
|
119
|
+
source="mcp"
|
|
120
|
+
target="aurora"
|
|
121
|
+
/>
|
|
122
|
+
</CardGrid>
|
|
123
|
+
|
|
124
|
+
:::note[Runtime Configuration]
|
|
125
|
+
The connection generator makes use of <Link path="guides/runtime-config">Runtime Configuration</Link> to pass deploy-time values (such as API URLs, Cognito settings, and agent runtime ARNs) between generated projects and components at runtime so they can discover and connect to one another.
|
|
126
|
+
:::
|
|
@@ -0,0 +1,403 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Docker Bundling
|
|
3
|
+
description: Build and deploy Docker images for TypeScript and Python projects in an Nx Plugin for AWS workspace.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
import { FileTree, Tabs, TabItem } from '@astrojs/starlight/components';
|
|
7
|
+
import NxCommands from '@components/nx-commands.astro';
|
|
8
|
+
import Link from '@components/link.astro';
|
|
9
|
+
import Infrastructure from '@components/infrastructure.astro';
|
|
10
|
+
|
|
11
|
+
Several generators (such as <Link path="/guides/ts-agent">`ts#agent`</Link> and <Link path="/guides/py-agent">`py#agent`</Link>) produce a Docker image that is pushed to Amazon ECR and consumed by AWS infrastructure. This guide describes the pattern they follow so that you can apply it to other use cases — for example, running a <Link path="/guides/fastapi">`py#fast-api`</Link> project on Amazon ECS, or deploying a containerised Express server.
|
|
12
|
+
|
|
13
|
+
:::tip[Docker or Finch]
|
|
14
|
+
The container engine used to build images is chosen at workspace creation time via the `--containerEngine` flag (`docker`, `finch`, or `infer` — the default — which auto-detects what's installed). [Finch](https://runfinch.com/) is an open-source, drop-in alternative to Docker. The selection is recorded in `aws-nx-plugin.config.mts` and applied to every generator that emits container build commands. CDK image asset builds honour the choice via the `CDK_DOCKER` environment variable.
|
|
15
|
+
:::
|
|
16
|
+
|
|
17
|
+
## The Pattern
|
|
18
|
+
|
|
19
|
+
```d2
|
|
20
|
+
direction: right
|
|
21
|
+
|
|
22
|
+
src: src/
|
|
23
|
+
dockerfile: Dockerfile {
|
|
24
|
+
shape: document
|
|
25
|
+
}
|
|
26
|
+
bundle: "bundle target\nRolldown / uv"
|
|
27
|
+
ctx: "dist/.../bundle/\n(build context)" {
|
|
28
|
+
shape: page
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
src -> bundle -> ctx
|
|
32
|
+
dockerfile -> ctx
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
The recommended pattern has three pieces:
|
|
36
|
+
|
|
37
|
+
1. **A `bundle` target** on your project which produces a self-contained directory of runtime artifacts. For TypeScript this is a tree-shaken, single-file JavaScript bundle produced by [Rolldown](https://rolldown.rs/); for Python this is a `requirements.txt` and installed dependencies produced by [uv](https://docs.astral.sh/uv/).
|
|
38
|
+
2. **A minimal `Dockerfile`** which simply `COPY`s the bundle output into a base image. Because bundling already handled tree-shaking and dependency installation, the `Dockerfile` does not need to run `npm install` or `uv sync`.
|
|
39
|
+
3. **A `docker` target** which copies the `Dockerfile` alongside the bundle output (so that the Docker build context only contains files needed at runtime), then runs `docker build`.
|
|
40
|
+
|
|
41
|
+
The Docker build context is written to your project's `dist` folder. Your infrastructure as code (CDK or Terraform) then points at that directory to push the image to ECR.
|
|
42
|
+
|
|
43
|
+
:::tip[Why bundle outside the Dockerfile?]
|
|
44
|
+
Keeping the bundle step in Nx (rather than inside the `Dockerfile`) means:
|
|
45
|
+
|
|
46
|
+
- Nx can **cache** the bundle target, so repeated builds are fast.
|
|
47
|
+
- The `Dockerfile` does not need access to your monorepo, private registries, or build-time secrets.
|
|
48
|
+
- The image layer is tiny — a single `COPY` of already-built artifacts, with no transitive `node_modules` or build toolchain.
|
|
49
|
+
:::
|
|
50
|
+
|
|
51
|
+
## TypeScript
|
|
52
|
+
|
|
53
|
+
### Bundle Target
|
|
54
|
+
|
|
55
|
+
Configure a `bundle` target that invokes Rolldown. If you are starting from a <Link path="/guides/typescript-project">`ts#project`</Link>, add the following to your `project.json`:
|
|
56
|
+
|
|
57
|
+
```json
|
|
58
|
+
{
|
|
59
|
+
"targets": {
|
|
60
|
+
"bundle": {
|
|
61
|
+
"cache": true,
|
|
62
|
+
"executor": "nx:run-commands",
|
|
63
|
+
"outputs": ["{workspaceRoot}/dist/{projectRoot}/bundle"],
|
|
64
|
+
"options": {
|
|
65
|
+
"command": "rolldown -c rolldown.config.ts",
|
|
66
|
+
"cwd": "{projectRoot}"
|
|
67
|
+
},
|
|
68
|
+
"dependsOn": ["compile"]
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
And a `rolldown.config.ts` at the root of your project:
|
|
75
|
+
|
|
76
|
+
```ts
|
|
77
|
+
// rolldown.config.ts
|
|
78
|
+
import { defineConfig } from 'rolldown';
|
|
79
|
+
|
|
80
|
+
export default defineConfig([
|
|
81
|
+
{
|
|
82
|
+
tsconfig: 'tsconfig.lib.json',
|
|
83
|
+
input: 'src/index.ts',
|
|
84
|
+
output: {
|
|
85
|
+
file: '../../dist/packages/my-project/bundle/index.js',
|
|
86
|
+
format: 'cjs',
|
|
87
|
+
inlineDynamicImports: true,
|
|
88
|
+
},
|
|
89
|
+
platform: 'node',
|
|
90
|
+
},
|
|
91
|
+
]);
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
Run the bundle target to produce `dist/packages/my-project/bundle/index.js`:
|
|
95
|
+
|
|
96
|
+
<NxCommands commands={['bundle my-project']} />
|
|
97
|
+
|
|
98
|
+
:::tip[Non-bundleable dependencies]
|
|
99
|
+
Some npm packages cannot be bundled because they rely on dynamic `require`, native bindings, or runtime file-path resolution. Add them to the `external` array in `rolldown.config.ts` so they are left as runtime `require()` calls, and install them inside the `Dockerfile` instead (see below):
|
|
100
|
+
|
|
101
|
+
```ts
|
|
102
|
+
{
|
|
103
|
+
// ...
|
|
104
|
+
external: ['@aws/aws-distro-opentelemetry-node-autoinstrumentation'],
|
|
105
|
+
}
|
|
106
|
+
```
|
|
107
|
+
:::
|
|
108
|
+
|
|
109
|
+
### Dockerfile
|
|
110
|
+
|
|
111
|
+
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:
|
|
112
|
+
|
|
113
|
+
```dockerfile
|
|
114
|
+
FROM public.ecr.aws/docker/library/node:lts
|
|
115
|
+
|
|
116
|
+
WORKDIR /app
|
|
117
|
+
|
|
118
|
+
# Install packages that cannot be bundled (declared as "external" in rolldown.config.ts).
|
|
119
|
+
# Kept above the COPY so this layer is cached and only invalidated when the install list changes.
|
|
120
|
+
RUN npm install @aws/aws-distro-opentelemetry-node-autoinstrumentation@0.10.0
|
|
121
|
+
|
|
122
|
+
# Copy bundled application
|
|
123
|
+
COPY index.js /app
|
|
124
|
+
|
|
125
|
+
EXPOSE 8080
|
|
126
|
+
|
|
127
|
+
CMD ["node", "index.js"]
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
:::tip[Pin your dependencies]
|
|
131
|
+
Pin every dependency installed inside the `Dockerfile` to an exact version (as above) — otherwise `npm install` will silently pick up a newer release the next time you build, producing images that behave differently depending on when they were built.
|
|
132
|
+
:::
|
|
133
|
+
|
|
134
|
+
### Docker Target
|
|
135
|
+
|
|
136
|
+
Add a `docker` target which:
|
|
137
|
+
|
|
138
|
+
1. Copies the `Dockerfile` into the bundle output directory (so the build context contains only the bundle + `Dockerfile`), and
|
|
139
|
+
2. Runs `docker build` (optional for CDK — see below).
|
|
140
|
+
|
|
141
|
+
```json
|
|
142
|
+
{
|
|
143
|
+
"targets": {
|
|
144
|
+
"docker": {
|
|
145
|
+
"cache": true,
|
|
146
|
+
"executor": "nx:run-commands",
|
|
147
|
+
"options": {
|
|
148
|
+
"commands": [
|
|
149
|
+
"ncp packages/my-project/src/Dockerfile dist/packages/my-project/bundle/Dockerfile",
|
|
150
|
+
"docker build --platform linux/arm64 -t my-scope-my-project:latest dist/packages/my-project/bundle"
|
|
151
|
+
],
|
|
152
|
+
"parallel": false
|
|
153
|
+
},
|
|
154
|
+
"dependsOn": ["bundle"]
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
:::tip[Cross-platform file copy]
|
|
161
|
+
The `ncp` package provides a cross-platform file/directory copy command, avoiding `cp` (unavailable on Windows). Install it at the root of your workspace with `pnpm add -D -w ncp`.
|
|
162
|
+
:::
|
|
163
|
+
|
|
164
|
+
Running this target produces a local image tagged `my-scope-my-project:latest`, built from the minimal context at `dist/packages/my-project/bundle/`:
|
|
165
|
+
|
|
166
|
+
<NxCommands commands={['docker my-project']} />
|
|
167
|
+
|
|
168
|
+
:::note[CDK builds the image itself]
|
|
169
|
+
If you are only deploying with CDK, the `docker build` command above is optional — CDK's `DockerImageAsset` (shown under [Infrastructure](#infrastructure)) will build the image for you at synth time. You still need to copy the `Dockerfile` into the build-context directory so CDK can find it. The generators keep the local `docker build` step to give you a quick way to smoke-test the image with `docker run`.
|
|
170
|
+
|
|
171
|
+
For Terraform, the `docker build` step is required — the `null_resource` pattern below pushes a locally-tagged image.
|
|
172
|
+
:::
|
|
173
|
+
|
|
174
|
+
## Python
|
|
175
|
+
|
|
176
|
+
### Bundle Target
|
|
177
|
+
|
|
178
|
+
Configure a `bundle` target that uses `uv` to export and install dependencies for your target platform. The <Link path="/guides/python-project">`py#project`</Link> generator and the <Link path="/guides/python-lambda-function">`py#lambda-function`</Link> generator both configure this for you. The target configuration looks like:
|
|
179
|
+
|
|
180
|
+
```json
|
|
181
|
+
{
|
|
182
|
+
"targets": {
|
|
183
|
+
"bundle-arm": {
|
|
184
|
+
"cache": true,
|
|
185
|
+
"executor": "nx:run-commands",
|
|
186
|
+
"outputs": ["{workspaceRoot}/dist/{projectRoot}/bundle-arm"],
|
|
187
|
+
"options": {
|
|
188
|
+
"commands": [
|
|
189
|
+
"uv export --frozen --no-dev --no-editable --project {projectRoot} --package my_project -o dist/{projectRoot}/bundle-arm/requirements.txt",
|
|
190
|
+
"uv pip install -n --no-deps --no-installer-metadata --no-compile-bytecode --python-platform aarch64-manylinux_2_28 --target dist/{projectRoot}/bundle-arm -r dist/{projectRoot}/bundle-arm/requirements.txt"
|
|
191
|
+
],
|
|
192
|
+
"parallel": false
|
|
193
|
+
},
|
|
194
|
+
"dependsOn": ["compile"]
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
:::tip[Target architecture]
|
|
201
|
+
Change `--python-platform` to `x86_64-manylinux_2_28` if your Docker image will run on x86_64.
|
|
202
|
+
:::
|
|
203
|
+
|
|
204
|
+
Running `nx bundle my-project` produces `dist/packages/my-project/bundle-arm/` containing your project's source, its dependencies, and a `requirements.txt` — everything the image needs at runtime.
|
|
205
|
+
|
|
206
|
+
### Dockerfile
|
|
207
|
+
|
|
208
|
+
The `Dockerfile` simply 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:
|
|
209
|
+
|
|
210
|
+
```dockerfile
|
|
211
|
+
FROM public.ecr.aws/docker/library/python:3.14-slim
|
|
212
|
+
|
|
213
|
+
WORKDIR /app
|
|
214
|
+
|
|
215
|
+
# Copy bundled package (source + installed dependencies)
|
|
216
|
+
COPY . /app
|
|
217
|
+
|
|
218
|
+
EXPOSE 8080
|
|
219
|
+
|
|
220
|
+
ENV PYTHONPATH=/app
|
|
221
|
+
ENV PATH="/app/bin:${PATH}"
|
|
222
|
+
|
|
223
|
+
CMD ["python", "-m", "my_project.main"]
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
### Docker Target
|
|
227
|
+
|
|
228
|
+
Add a `docker` target which copies the `Dockerfile` into the bundle output directory, then runs `docker build`:
|
|
229
|
+
|
|
230
|
+
```json
|
|
231
|
+
{
|
|
232
|
+
"targets": {
|
|
233
|
+
"docker": {
|
|
234
|
+
"cache": true,
|
|
235
|
+
"executor": "nx:run-commands",
|
|
236
|
+
"options": {
|
|
237
|
+
"commands": [
|
|
238
|
+
"rimraf dist/packages/my-project/docker",
|
|
239
|
+
"make-dir dist/packages/my-project/docker",
|
|
240
|
+
"ncp dist/packages/my-project/bundle-arm dist/packages/my-project/docker",
|
|
241
|
+
"ncp packages/my-project/src/Dockerfile dist/packages/my-project/docker/Dockerfile",
|
|
242
|
+
"docker build --platform linux/arm64 -t my-scope-my-project:latest dist/packages/my-project/docker"
|
|
243
|
+
],
|
|
244
|
+
"parallel": false
|
|
245
|
+
},
|
|
246
|
+
"dependsOn": ["bundle-arm"]
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
This clears the output directory, then copies both the bundle contents and the `Dockerfile` into `dist/.../docker`, which becomes the Docker build context.
|
|
253
|
+
|
|
254
|
+
<NxCommands commands={['docker my-project']} />
|
|
255
|
+
|
|
256
|
+
## Infrastructure
|
|
257
|
+
|
|
258
|
+
Wiring the resulting build-context directory to infrastructure as code is the same for both TypeScript and Python — only the path to the build-context directory differs (`dist/packages/my-project/bundle` for TypeScript, `dist/packages/my-project/docker` for Python).
|
|
259
|
+
|
|
260
|
+
<Infrastructure>
|
|
261
|
+
<Fragment slot="cdk">
|
|
262
|
+
Use CDK's [`DockerImageAsset`](https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_ecr_assets.DockerImageAsset.html) pointed at the build-context directory. CDK will build the image and publish it to the CDK asset ECR repository at deploy time:
|
|
263
|
+
|
|
264
|
+
```d2
|
|
265
|
+
direction: right
|
|
266
|
+
|
|
267
|
+
app: Application {
|
|
268
|
+
bundle: "bundle target\nRolldown / uv"
|
|
269
|
+
dockerfile: Dockerfile {
|
|
270
|
+
shape: document
|
|
271
|
+
}
|
|
272
|
+
ctx: "dist/.../bundle/" {
|
|
273
|
+
shape: page
|
|
274
|
+
}
|
|
275
|
+
bundle -> ctx
|
|
276
|
+
dockerfile -> ctx
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
infra: Infrastructure {
|
|
280
|
+
asset: "DockerImageAsset\ndirectory: dist/.../bundle"
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
ecr: "CDK asset ECR" {
|
|
284
|
+
shape: cylinder
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
infra.asset -> app.ctx: references
|
|
288
|
+
infra.asset -> ecr: cdk deploy\nbuilds + pushes
|
|
289
|
+
```
|
|
290
|
+
|
|
291
|
+
|
|
292
|
+
```ts
|
|
293
|
+
import { DockerImageAsset, Platform } from 'aws-cdk-lib/aws-ecr-assets';
|
|
294
|
+
import { findWorkspaceRoot } from ':my-scope/common-constructs';
|
|
295
|
+
import * as path from 'path';
|
|
296
|
+
import * as url from 'url';
|
|
297
|
+
|
|
298
|
+
const image = new DockerImageAsset(this, 'MyImage', {
|
|
299
|
+
directory: path.join(
|
|
300
|
+
// Resolve from the compiled construct location to the workspace root
|
|
301
|
+
findWorkspaceRoot(url.fileURLToPath(new URL(import.meta.url))),
|
|
302
|
+
'dist/packages/my-project/bundle',
|
|
303
|
+
),
|
|
304
|
+
platform: Platform.LINUX_ARM64,
|
|
305
|
+
});
|
|
306
|
+
```
|
|
307
|
+
|
|
308
|
+
The `findWorkspaceRoot` helper is generated by the <Link path="/guides/typescript-infrastructure">`ts#infra`</Link> generator and exported from `:my-scope/common-constructs`. If you are not using shared constructs, you can hardcode the path to the `dist` directory relative to where `cdk` is invoked from — typically the workspace root — and omit the `findWorkspaceRoot` call entirely.
|
|
309
|
+
|
|
310
|
+
:::note[Running bundle before synth]
|
|
311
|
+
CDK does not run the `bundle`/`docker` targets automatically — you must run `nx build my-project` (or wire the deploy target to depend on `build`) before `cdk deploy`. The generators that use this pattern declare `docker` and `bundle` as dependencies of `build` so this happens transparently.
|
|
312
|
+
:::
|
|
313
|
+
|
|
314
|
+
Use the `DockerImageAsset` with any AWS construct that accepts a container image, for example `aws_ecs.ContainerImage.fromDockerImageAsset(image)`.
|
|
315
|
+
</Fragment>
|
|
316
|
+
<Fragment slot="terraform">
|
|
317
|
+
Terraform's AWS provider does not have a first-class "build and push a Docker image" resource. The pattern used by the generators is:
|
|
318
|
+
|
|
319
|
+
1. The project's `build` target runs `docker build`, producing a local image tagged `my-scope-my-project:latest`.
|
|
320
|
+
2. An `aws_ecr_repository` to hold the image.
|
|
321
|
+
3. A `null_resource` with a `local-exec` provisioner that authenticates to ECR, re-tags the locally-built image, and pushes it.
|
|
322
|
+
4. The downstream resource (e.g. `aws_ecs_task_definition`) references `"${aws_ecr_repository.repo.repository_url}:latest"`.
|
|
323
|
+
|
|
324
|
+
```d2
|
|
325
|
+
direction: down
|
|
326
|
+
|
|
327
|
+
app: Application {
|
|
328
|
+
bundle: "bundle target\nRolldown / uv"
|
|
329
|
+
dockerfile: Dockerfile {
|
|
330
|
+
shape: document
|
|
331
|
+
}
|
|
332
|
+
ctx: "dist/.../bundle/" {
|
|
333
|
+
shape: page
|
|
334
|
+
}
|
|
335
|
+
build: "docker target\ndocker build"
|
|
336
|
+
img: "local image\nmy-scope-my-project:latest" {
|
|
337
|
+
shape: cylinder
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
bundle -> ctx
|
|
341
|
+
dockerfile -> ctx
|
|
342
|
+
ctx -> build -> img
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
infra: Infrastructure {
|
|
346
|
+
direction: right
|
|
347
|
+
publish: "null_resource\nlocal-exec: docker push"
|
|
348
|
+
repo: aws_ecr_repository
|
|
349
|
+
publish -> repo: tag + push
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
ecr: "Amazon ECR" {
|
|
353
|
+
shape: cylinder
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
infra.publish -> app.img: reads local image
|
|
357
|
+
infra.repo -> ecr
|
|
358
|
+
```
|
|
359
|
+
|
|
360
|
+
|
|
361
|
+
```hcl
|
|
362
|
+
resource "aws_ecr_repository" "repo" {
|
|
363
|
+
name = "my-project-repository"
|
|
364
|
+
image_tag_mutability = "MUTABLE"
|
|
365
|
+
force_delete = true
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
# Invalidate the push whenever the locally-built image digest changes
|
|
369
|
+
data "external" "docker_digest" {
|
|
370
|
+
program = ["sh", "-c", "echo '{\"digest\":\"'$(docker inspect my-scope-my-project:latest --format '{{.Id}}')'\"}'"]
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
resource "null_resource" "docker_publish" {
|
|
374
|
+
triggers = {
|
|
375
|
+
docker_digest = data.external.docker_digest.result.digest
|
|
376
|
+
repository_url = aws_ecr_repository.repo.repository_url
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
provisioner "local-exec" {
|
|
380
|
+
command = <<-EOT
|
|
381
|
+
aws ecr get-login-password --region ${data.aws_region.current.id} \
|
|
382
|
+
| docker login --username AWS --password-stdin ${self.triggers.repository_url}
|
|
383
|
+
docker tag my-scope-my-project:latest ${self.triggers.repository_url}:latest
|
|
384
|
+
docker push ${self.triggers.repository_url}:latest
|
|
385
|
+
EOT
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
```
|
|
389
|
+
|
|
390
|
+
The `data.external.docker_digest` block ensures the `null_resource` re-runs whenever the local image hash changes, triggering a new push on every meaningful code change.
|
|
391
|
+
|
|
392
|
+
:::note[Running bundle before apply]
|
|
393
|
+
`nx apply <project>` requires the image tag `my-scope-my-project:latest` to already exist locally. Run `nx build my-project` (or `nx docker my-project`) before `nx apply <project>`.
|
|
394
|
+
:::
|
|
395
|
+
</Fragment>
|
|
396
|
+
</Infrastructure>
|
|
397
|
+
|
|
398
|
+
## Further Reading
|
|
399
|
+
|
|
400
|
+
- <Link path="/guides/ts-agent">`ts#agent` generator</Link> — a complete example of this pattern for a TypeScript agent deployed to Bedrock AgentCore Runtime.
|
|
401
|
+
- <Link path="/guides/py-agent">`py#agent` generator</Link> — the equivalent for Python.
|
|
402
|
+
- [Rolldown documentation](https://rolldown.rs/) — configuration reference for the TypeScript bundler.
|
|
403
|
+
- [`uv` documentation](https://docs.astral.sh/uv/) — reference for Python dependency export and install.
|