Skip to content

Add to an Existing Project

Not every project starts with pnpm create @aws/nx-workspace. If you already have an Nx workspace — or a monorepo you can add Nx to — you can adopt @aws/nx-plugin incrementally without recreating your project.

  • Git
  • Node >= 22 (We recommend using something like NVM to manage your node versions)
    • verify by running node --version
  • UV >= 0.5.29
    1. install Python 3.14 by running: uv python install 3.14
    2. verify with uv python list --only-installed

If your project doesn’t use Nx yet, add it first with nx init. This is a step Nx itself owns; the plugin builds on top of a working Nx workspace. It works on a plain package, an npm/pnpm/yarn/bun workspace, a Turborepo or a Lerna monorepo:

Terminal window
pnpm dlx nx@23.2.0 init

Follow the prompts to add Nx to your workspace. For more details refer to the Nx Documentation.

Non-Node projects (Python, Go, Java, Rust, …)

Section titled “Non-Node projects (Python, Go, Java, Rust, …)”

Nx and the plugin are distributed as npm packages, so a project with no Node.js tooling needs a minimal root package.json before nx init can do anything useful:

package.json
{
"name": "my-project",
"private": true,
"type": "module"
}

Create that file, then run nx init and add the plugin as usual. Your existing language tooling is untouched — Nx projects generated by the plugin (including Python projects) live alongside your existing code, and you can wire your existing build into Nx incrementally.

To bring your existing projects under Nx, you have a few options depending on the language:

  • Languages with an official Nx pluginJava (Gradle or Maven) and .NET have dedicated plugins that infer your projects’ tasks automatically. Add the relevant one, e.g. nx add @nx/gradle, nx add @nx/maven or nx add @nx/dotnet.
  • Languages with a community plugin — for example Go (@nx-go/nx-go) or Rust (@monodon/rust). Browse the Nx Plugin Registry for others.
  • Any other language — add a project.json to each project and define its targets to run your existing build commands, so nx build <project> (and nx run-many) drive them. See the workspace guide for how project.json and targets are set up.

The plugin is designed for monorepos — it generates each project into its own directory under packages/. If you’re starting from a single-package project (one package.json at the root with your source directly beneath it, no workspaces), move your existing package into packages/ before adopting the plugin so your project sits alongside the ones the plugin generates:

  1. Create a packages/<your-package>/ directory and move your source, package.json and tsconfig.json into it.

  2. Create a new root package.json that acts as the workspace manifest rather than a project itself:

    package.json
    {
    "name": "<your-workspace>",
    "private": true,
    "type": "module"
    }
  3. Declare the workspace so your package manager discovers projects under packages/. For pnpm, add a pnpm-workspace.yaml:

    pnpm-workspace.yaml
    packages:
    - packages/*

    For npm/yarn/bun, add a workspaces field to the root package.json instead:

    package.json
    {
    "workspaces": ["packages/*"]
    }
  4. Run nx init (if you haven’t), then add the plugin.

Use nx add, which installs the plugin at a version compatible with your Nx installation and then runs its init generator to configure your workspace:

Terminal window
pnpm nx add @aws/nx-plugin

Once it completes, your workspace is ready — choose the generators you need and start generating projects.

Adding the plugin makes the deterministic changes a workspace needs to run the plugin’s generators. It preserves your workspace’s module format: a workspace whose root package.json has type: "module" stays ESM, anything else stays CommonJS, and generated code follows suit. It does not overwrite existing files, and you may need to make some manual changes following this step depending on your existing configuration.

It creates or updates the following files:

  • aws-nx-plugin.config.mts records your chosen IaC provider (CDK or Terraform) and container engine; generators read iac.provider from here
  • nx.json registers the sync generators (@nx/js:typescript-sync and @aws/nx-plugin:ts#sync) on the compile target so TypeScript project references stay in sync
  • tsconfig.json a root TypeScript config referencing your workspace’s projects, which Nx’s TypeScript sync keeps up to date
  • tsconfig.base.json the shared compiler options the plugin’s TypeScript projects extend (see below for what it carries)
  • pnpm-workspace.yaml (pnpm only) allow-lists the build scripts the plugin’s dependencies need (@swc/core, esbuild, nx, sharp)
  • package.json convenience scripts for common tasks, a prepare script that installs the git hooks, plus the @aws/nx-plugin, @aws/nx-plugin-mcp, nx, @nx/js, @nx/workspace, typescript, husky and Biome dev dependencies
  • biome.json default Biome formatter and linter configuration
  • Directory.git-secrets/ the vendored git-secrets script, with .gitallowed for false positives and a .husky/pre-commit hook that runs it — see Git Secrets
  • .mcp.json configures the MCP server for supported coding agents unless disabled (also .cursor/, .kiro/, .gemini/, .vscode/ and .codex/ equivalents). It runs the workspace’s own @aws/nx-plugin-mcp, so the server always matches the version installed in your workspace

The MCP server configuration can be disabled with --mcp=false, and the credential scanning hooks with --gitSecrets=false.

Files you already have are preserved: an existing .husky/pre-commit hook or prepare script is left as it is, and the generator prints what to add so credential scanning runs alongside your own checks. An existing .gitallowed keeps every pattern it has.

Click here to see the compiler options a created tsconfig.base.json carries.
Generator Options5 options
iacenumDefault: cdk

The preferred IaC provider.

cdkterraform
containersenumDefault: infer

The container engine to use for build/push/login. 'infer' picks docker if installed, otherwise finch (falling back to docker when neither is installed).

inferdockerfinch
gitSecretsbooleanDefault: true

Whether to configure git-secrets to prevent committing AWS credentials.

mcpbooleanDefault: true

Whether to configure the Nx Plugin for AWS MCP server for use by coding agents.

preferInstallDependenciesbooleanDefault: true

Whether to prefer installing dependencies after the generator runs. Set to false to defer installing when batching multiple generators (an install still runs if needed so subsequent generators can compute the Nx project graph); install once at the end.

nx build <project> no longer works for an existing TypeScript library

Section titled “nx build <project> no longer works for an existing TypeScript library”

The first ts#project run configures the @nx/js/typescript plugin to infer the TypeScript build target as compile (the plugin reserves build for orchestrating lint, compile and test). Existing libraries relying on the inferred build target are inferred as compile too — run nx run <project>:compile, or use nx run-many --target build,compile. Libraries with an explicit build target in their project.json are unaffected.

TypeScript project references need syncing. init registers the sync generators; run:

Terminal window
pnpm nx sync

pnpm skips install scripts for packages that aren’t allow-listed. init allow-lists the ones the plugin’s tooling needs (@swc/core, esbuild, nx, sharp) in pnpm-workspace.yaml, but the block can fire during nx add @aws/nx-plugin itself — before init has run. Run pnpm approve-builds and approve the listed packages (or add them under allowBuilds:), then re-run the install.

nx sync hangs, or plugin worker ... exited before the connection was established

Section titled “nx sync hangs, or plugin worker ... exited before the connection was established”

If two different nx versions are present in the same node_modules tree (run npm ls nx to confirm), their plugin-worker IPC deadlocks. init declares the nx and @nx/* versions the plugin is built against, but it never downgrades a version your workspace already has: a workspace already on a newer nx keeps it, and the plugin’s own @nx/* packages pull in their own copy alongside it. Align every nx and @nx/* entry in your root package.json — and, where your package manager uses one, the dependency catalog — to a single version, then reinstall.

Your workspace has TypeScript 7 installed, which Nx does not yet support — Nx’s plugins rely on TypeScript’s in-process compiler API, which TypeScript 7 no longer provides. Pin typescript in your root package.json to the version the plugin uses (the init generator installs a compatible version) and reinstall.

Your root package.json is registered as an Nx project (it has an "nx" key, which nx init adds for a single-package repo) and carries a build script of nx run-many --target build. Nx then infers a build target on the root that calls itself. Move your source into packages/<your-package>/ so the root becomes a workspace manifest rather than a project — see Single-package projects. (Nx’s own standalone presets avoid this by setting "nx": { "includedScripts": [] } on the root package; you can do the same as a quicker alternative.)

TypeScript errors in your existing projects after adding the plugin (TS6059, TS7016, TS5011, NG4006)

Section titled “TypeScript errors in your existing projects after adding the plugin (TS6059, TS7016, TS5011, NG4006)”

The plugin’s own generated projects carry the TypeScript settings they need in their per-project tsconfig.lib.json, so they build regardless of your tsconfig.base.json. These errors show up instead in a pre-existing project of yours that inherits a tsconfig.base.json init created (with composite/emitDeclarationOnly/nodenext) but isn’t compatible with those settings — for example, the Angular compiler rejects emitDeclarationOnly (NG4006), or a project that sets outDir without a rootDir now needs one (TS5011). Add per-project tsconfig overrides re-declaring the conflicting options for those projects (e.g. "rootDir": "src"), or point the plugin’s projects and your projects at separate base configs.

init never rewrites an existing tsconfig.base.json, precisely so it can’t silently break the projects that inherit from it — resolving these mismatches is a decision only you can make for your codebase.