From 1fa19b0d41cb0109a628c075c0fc67b86fd87cbf Mon Sep 17 00:00:00 2001 From: Fredrik Enestad Date: Mon, 26 Jan 2026 17:00:41 +0100 Subject: [PATCH 1/6] add turborepo setup guide --- docs/ts/develop/monorepo/turborepo.md | 274 ++++++++++++++++++++++++++ 1 file changed, 274 insertions(+) create mode 100644 docs/ts/develop/monorepo/turborepo.md diff --git a/docs/ts/develop/monorepo/turborepo.md b/docs/ts/develop/monorepo/turborepo.md new file mode 100644 index 0000000000..86b504bc77 --- /dev/null +++ b/docs/ts/develop/monorepo/turborepo.md @@ -0,0 +1,274 @@ +--- +seotitle: Using Encore with Turborepo in a monorepo +seodesc: Learn how to set up Encore.ts in a Turborepo monorepo with shared packages that require building before use. +title: Turborepo +subtitle: Using Encore in a Turborepo monorepo +lang: ts +--- + +[Turborepo](https://turbo.build/repo) is a build system for JavaScript and TypeScript monorepos. This guide shows how to set up an Encore application within a Turborepo monorepo that depends on shared packages requiring compilation. + +## Overview + +When using Encore in a Turborepo monorepo, you may have shared packages (like utility libraries or shared types) that need to be built before the Encore app can use them. Since Encore parses your application on startup, these dependencies must be compiled first. + +This guide covers two scenarios: +- **Local development**: Use Turborepo to build dependencies before running `encore run` +- **Deployment**: Use Encore's `prebuild` hook to automatically build dependencies when deploying via Encore Cloud or exporting a Docker image + +## Project structure + +A typical Turborepo setup with Encore looks like this: + +``` +my-turborepo/ +├── apps/ +│ └── backend/ # Encore application +│ ├── encore.app +│ ├── package.json +│ ├── tsconfig.json +│ └── article/ +│ └── article.ts +├── packages/ +│ └── shared/ # Shared library requiring build +│ ├── package.json +│ ├── tsconfig.json +│ ├── src/ +│ │ └── index.ts +│ └── dist/ # Built output +│ └── index.js +├── turbo.json +├── package.json +└── package-lock.json +``` + +## Configuration + +### Root package.json + +Configure npm workspaces to include your apps and packages: + +```json +{ + "name": "my-turborepo", + "private": true, + "packageManager": "npm@10.0.0", + "scripts": { + "build": "turbo run build", + "dev": "turbo run dev" + }, + "devDependencies": { + "turbo": "^2.0.0", + "typescript": "^5.0.0" + }, + "workspaces": [ + "apps/*", + "packages/*" + ] +} +``` + +The `packageManager` field is required by Turborepo. Adjust the version to match your installed npm version (run `npm --version` to check). + +### turbo.json + +Configure Turborepo's build pipeline in the root `turbo.json`. The `@repo/backend#dev` task depends on the shared package being built first: + +```json +{ + "$schema": "https://turbo.build/schema.json", + "tasks": { + "build": { + "dependsOn": ["^build"], + "outputs": ["dist/**"] + }, + "@repo/backend#dev": { + "dependsOn": ["@repo/shared#build"], + "cache": false, + "persistent": true + }, + "dev": { + "cache": false, + "persistent": true + } + } +} +``` + +The `@repo/backend#dev` task configuration ensures the shared package is built before running `encore run` in local development. + +### Shared package + +Your shared package needs to compile TypeScript to JavaScript and expose the built output: + +**packages/shared/package.json:** +```json +{ + "name": "@repo/shared", + "version": "1.0.0", + "private": true, + "type": "module", + "main": "./dist/index.js", + "types": "./dist/index.d.ts", + "exports": { + ".": { + "types": "./dist/index.d.ts", + "default": "./dist/index.js" + } + }, + "scripts": { + "build": "tsc" + }, + "devDependencies": { + "typescript": "^5.0.0" + } +} +``` + +**packages/shared/tsconfig.json:** +```json +{ + "compilerOptions": { + "outDir": "dist", + "rootDir": "src", + "moduleResolution": "bundler", + "module": "ES2022", + "target": "ES2022", + "declaration": true + }, + "include": ["src"], + "exclude": ["node_modules", "dist"] +} +``` + +**packages/shared/src/index.ts:** +```ts +// Types shared between frontend and backend +export interface Article { + slug: string; + title: string; + preview: string; +} + +export interface CreateArticleRequest { + title: string; + content: string; +} + +// Utility functions +export function slugify(text: string): string { + return text + .toLowerCase() + .trim() + .replace(/[^\w\s-]/g, "") + .replace(/\s+/g, "-"); +} + +export function truncate(text: string, maxLength: number): string { + if (text.length <= maxLength) return text; + return text.slice(0, maxLength - 3) + "..."; +} +``` + +### Encore application + +The Encore app needs two key configurations: + +1. **encore.app** - Use the `prebuild` hook to build dependencies during deployment +2. **package.json** - Declare the dependency on the shared package + +To create the Encore app, run `encore app init --lang ts` from the `apps/backend` directory. Then add the `prebuild` hook to the generated `encore.app` file: + +**apps/backend/encore.app:** +```json +{ + "id": "generated-id", + "lang": "typescript", + "build": { + "hooks": { + "prebuild": "npx turbo build --filter=@repo/backend..." + } + } +} +``` + +The `prebuild` hook runs when deploying via Encore Cloud or when exporting a Docker image with the Encore CLI. The filter `@repo/backend...` tells Turborepo to build `@repo/backend` and all its dependencies, ensuring the shared package is compiled before Encore builds your application. + +**apps/backend/package.json:** +```json +{ + "name": "@repo/backend", + "version": "1.0.0", + "type": "module", + "scripts": { + "dev": "encore run" + }, + "dependencies": { + "@repo/shared": "*", + "encore.dev": "latest" + } +} +``` + +### Using the shared package + +With this setup, you can import from your shared package in your Encore services: + +**apps/backend/article/article.ts:** +```ts +import { api } from "encore.dev/api"; +import type { Article, CreateArticleRequest } from "@repo/shared"; +import { slugify, truncate } from "@repo/shared"; + +export const create = api( + { expose: true, method: "POST", path: "/article" }, + async ({ title, content }: CreateArticleRequest): Promise
=> { + return { + slug: slugify(title), + title: title, + preview: truncate(content, 100), + }; + }, +); +``` + +## Running the application + +### Installation + +First, install all dependencies from the monorepo root: + +```shell +$ npm install +``` + +This installs dependencies for all workspaces, including Turborepo. + +### Local development + +For local development, you need to build the shared packages before running `encore run`. From the monorepo root: + +```shell +$ npx turbo run build +$ cd apps/backend && encore run +``` + +Or use Turborepo's `dev` task which handles the dependency ordering: + +```shell +$ npx turbo run dev --filter=@repo/backend +``` + +The `turbo.json` configuration ensures `@repo/shared` is built before the backend's dev task runs. + +### Deployment + +When deploying via Encore Cloud or exporting a Docker image, the `prebuild` hook in `encore.app` automatically runs the Turborepo build pipeline. No additional setup is needed. + +## Key points + +- **Local development**: Run `npx turbo run build` before `encore run`, or use `npx turbo run dev --filter=@repo/backend` to handle dependency ordering automatically +- **Prebuild hook**: The `prebuild` hook in `encore.app` runs during deployment (Encore Cloud) or Docker export, not during local development +- **Turborepo filter**: Using `--filter=@repo/backend...` builds the backend and all its dependencies +- **ESM modules**: Use `"type": "module"` in package.json files for ES module support +- **Exports field**: Define the `exports` field in shared packages to specify entry points for both types and JavaScript From 27d0871a4bb4298cf72510dd891a09870a7837fb Mon Sep 17 00:00:00 2001 From: Fredrik Enestad Date: Mon, 26 Jan 2026 17:40:04 +0100 Subject: [PATCH 2/6] add nx setup guide --- docs/ts/develop/monorepo/nx.md | 316 ++++++++++++++++++++++++++ docs/ts/develop/monorepo/turborepo.md | 6 +- 2 files changed, 319 insertions(+), 3 deletions(-) create mode 100644 docs/ts/develop/monorepo/nx.md diff --git a/docs/ts/develop/monorepo/nx.md b/docs/ts/develop/monorepo/nx.md new file mode 100644 index 0000000000..068ac7c00b --- /dev/null +++ b/docs/ts/develop/monorepo/nx.md @@ -0,0 +1,316 @@ +--- +seotitle: Using Encore with Nx in a monorepo +seodesc: Learn how to set up Encore.ts in an Nx monorepo with shared packages that require building before use. +title: Nx +subtitle: Using Encore in an Nx monorepo +lang: ts +--- + +[Nx](https://nx.dev) is a build system for JavaScript and TypeScript monorepos. This guide shows how to set up an Encore application within an Nx monorepo that depends on shared packages requiring compilation. + +## Overview + +When using Encore in an Nx monorepo, you may have shared packages (like utility libraries or shared types) that need to be built before the Encore app can use them. Since Encore parses your application on startup, these dependencies must be compiled first. + +This guide covers two scenarios: +- **Local development**: Use Nx to build dependencies before running `encore run` +- **Deployment**: Use Encore's `prebuild` hook to automatically build dependencies when deploying via Encore Cloud or exporting a Docker image + +## Project structure + +A typical Nx setup with Encore looks like this: + +``` +my-nx-workspace/ +├── apps/ +│ └── backend/ # Encore application +│ ├── encore.app +│ ├── package.json +│ ├── project.json +│ ├── tsconfig.json +│ └── article/ +│ └── article.ts +├── packages/ +│ └── shared/ # Shared library requiring build +│ ├── package.json +│ ├── project.json +│ ├── tsconfig.json +│ ├── src/ +│ │ └── index.ts +│ └── dist/ # Built output +│ └── index.js +├── nx.json +├── package.json +└── package-lock.json +``` + +## Configuration + +### Root package.json + +Configure npm workspaces to include your apps and packages: + +```json +{ + "name": "my-nx-workspace", + "private": true, + "scripts": { + "build": "nx run-many -t build", + "dev": "nx run-many -t dev" + }, + "devDependencies": { + "nx": "^21.0.0", + "typescript": "^5.0.0" + }, + "workspaces": [ + "apps/*", + "packages/*" + ] +} +``` + +### nx.json + +Configure Nx's build pipeline in the root `nx.json`: + +```json +{ + "$schema": "./node_modules/nx/schemas/nx-schema.json", + "targetDefaults": { + "build": { + "dependsOn": ["^build"], + "outputs": ["{projectRoot}/dist/**"], + "cache": true + }, + "dev": { + "cache": false + } + } +} +``` + +The `"dependsOn": ["^build"]` configuration ensures that a project's dependencies are built before the project itself. + +### Shared package + +Your shared package needs to compile TypeScript to JavaScript and expose the built output. + +**packages/shared/package.json:** +```json +{ + "name": "@repo/shared", + "version": "1.0.0", + "private": true, + "type": "module", + "main": "./dist/index.js", + "types": "./dist/index.d.ts", + "exports": { + ".": { + "types": "./dist/index.d.ts", + "default": "./dist/index.js" + } + }, + "scripts": { + "build": "tsc" + }, + "devDependencies": { + "typescript": "^5.0.0" + } +} +``` + +**packages/shared/project.json:** +```json +{ + "name": "@repo/shared", + "$schema": "../../node_modules/nx/schemas/project-schema.json", + "sourceRoot": "packages/shared/src", + "projectType": "library", + "targets": { + "build": { + "executor": "nx:run-commands", + "options": { + "command": "tsc", + "cwd": "packages/shared" + }, + "outputs": ["{projectRoot}/dist"] + } + } +} +``` + +**packages/shared/tsconfig.json:** +```json +{ + "compilerOptions": { + "outDir": "dist", + "rootDir": "src", + "moduleResolution": "bundler", + "module": "ES2022", + "target": "ES2022", + "declaration": true + }, + "include": ["src"], + "exclude": ["node_modules", "dist"] +} +``` + +**packages/shared/src/index.ts:** +```ts +// Types shared between frontend and backend +export interface Article { + slug: string; + title: string; + preview: string; +} + +export interface CreateArticleRequest { + title: string; + content: string; +} + +// Utility functions +export function slugify(text: string): string { + return text + .toLowerCase() + .trim() + .replace(/[^\w\s-]/g, "") + .replace(/\s+/g, "-"); +} + +export function truncate(text: string, maxLength: number): string { + if (text.length <= maxLength) return text; + return text.slice(0, maxLength - 3) + "..."; +} +``` + +### Encore application + +The Encore app needs three key configurations: + +1. **encore.app** - Use the `prebuild` hook to build dependencies during deployment +2. **package.json** - Declare the dependency on the shared package +3. **project.json** - Configure Nx targets and task dependencies + +To create the Encore app, run `encore app init --lang ts` from the `apps/backend` directory. Then add the `prebuild` hook to the generated `encore.app` file: + +**apps/backend/encore.app:** +```json +{ + "id": "generated-id", + "lang": "typescript", + "build": { + "hooks": { + "prebuild": "npx nx build-deps @repo/backend" + } + } +} +``` + +The `prebuild` hook runs when deploying via Encore Cloud or when exporting a Docker image with the Encore CLI. The `build-deps` target builds all dependencies of the backend. + +**apps/backend/package.json:** +```json +{ + "name": "@repo/backend", + "version": "1.0.0", + "type": "module", + "scripts": { + "dev": "encore run" + }, + "dependencies": { + "@repo/shared": "*", + "encore.dev": "latest" + } +} +``` + +**apps/backend/project.json:** +```json +{ + "name": "@repo/backend", + "$schema": "../../node_modules/nx/schemas/project-schema.json", + "sourceRoot": "apps/backend", + "projectType": "application", + "targets": { + "dev": { + "executor": "nx:run-commands", + "options": { + "command": "encore run", + "cwd": "apps/backend" + }, + "dependsOn": ["^build"], + "cache": false + }, + "build-deps": { + "dependsOn": ["^build"], + "cache": true + } + } +} +``` + +The `"dependsOn": ["^build"]` configuration uses the `^` prefix to indicate "run the build target on all dependencies first". This automatically builds all shared packages before running `encore run`, without needing to list each dependency explicitly. + +### Using the shared package + +With this setup, you can import from your shared package in your Encore services: + +**apps/backend/article/article.ts:** +```ts +import { api } from "encore.dev/api"; +import type { Article, CreateArticleRequest } from "@repo/shared"; +import { slugify, truncate } from "@repo/shared"; + +export const create = api( + { expose: true, method: "POST", path: "/article" }, + async ({ title, content }: CreateArticleRequest): Promise
=> { + return { + slug: slugify(title), + title: title, + preview: truncate(content, 100), + }; + }, +); +``` + +## Running the application + +### Installation + +First, install all dependencies from the monorepo root: + +```shell +$ npm install +``` + +This installs dependencies for all workspaces, including Nx. + +### Local development + +For local development, you need to build the shared packages before running `encore run`. From the monorepo root: + +```shell +$ npx nx build-deps @repo/backend +$ cd apps/backend && encore run +``` + +Or use Nx's `dev` target which handles the dependency ordering: + +```shell +$ npx nx dev @repo/backend +``` + +The `"dependsOn": ["^build"]` configuration ensures all dependencies are built before the backend's dev target runs. + +### Deployment + +When deploying via Encore Cloud or exporting a Docker image, the `prebuild` hook in `encore.app` automatically runs the Nx build. No additional setup is needed. + +## Key points + +- **Local development**: Run `npx nx build-deps @repo/backend` before `encore run`, or use `npx nx dev @repo/backend` to handle dependency ordering automatically +- **Prebuild hook**: The `prebuild` hook in `encore.app` runs during deployment (Encore Cloud) or Docker export, not during local development +- **Task dependencies**: Use `"dependsOn": ["^build"]` in `project.json` to automatically build all dependencies before running a target +- **ESM modules**: Use `"type": "module"` in package.json files for ES module support +- **Exports field**: Define the `exports` field in shared packages to specify entry points for both types and JavaScript diff --git a/docs/ts/develop/monorepo/turborepo.md b/docs/ts/develop/monorepo/turborepo.md index 86b504bc77..4dc1cb1602 100644 --- a/docs/ts/develop/monorepo/turborepo.md +++ b/docs/ts/develop/monorepo/turborepo.md @@ -186,13 +186,13 @@ To create the Encore app, run `encore app init --lang ts` from the `apps/backend "lang": "typescript", "build": { "hooks": { - "prebuild": "npx turbo build --filter=@repo/backend..." + "prebuild": "npx turbo build --filter=@repo/backend^..." } } } ``` -The `prebuild` hook runs when deploying via Encore Cloud or when exporting a Docker image with the Encore CLI. The filter `@repo/backend...` tells Turborepo to build `@repo/backend` and all its dependencies, ensuring the shared package is compiled before Encore builds your application. +The `prebuild` hook runs when deploying via Encore Cloud or when exporting a Docker image with the Encore CLI. The filter `@repo/backend^...` tells Turborepo to build all dependencies of `@repo/backend`. The `^` excludes the backend itself, building only its dependencies. **apps/backend/package.json:** ```json @@ -269,6 +269,6 @@ When deploying via Encore Cloud or exporting a Docker image, the `prebuild` hook - **Local development**: Run `npx turbo run build` before `encore run`, or use `npx turbo run dev --filter=@repo/backend` to handle dependency ordering automatically - **Prebuild hook**: The `prebuild` hook in `encore.app` runs during deployment (Encore Cloud) or Docker export, not during local development -- **Turborepo filter**: Using `--filter=@repo/backend...` builds the backend and all its dependencies +- **Turborepo filter**: Using `--filter=@repo/backend^...` builds only the dependencies of the backend (the `^` excludes the package itself) - **ESM modules**: Use `"type": "module"` in package.json files for ES module support - **Exports field**: Define the `exports` field in shared packages to specify entry points for both types and JavaScript From d0441a03d61c4b54559b8d34f0559bef896a9bab Mon Sep 17 00:00:00 2001 From: Fredrik Enestad Date: Mon, 26 Jan 2026 17:45:11 +0100 Subject: [PATCH 3/6] add menu --- docs/menu.cue | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/docs/menu.cue b/docs/menu.cue index 2e8df82d83..3f576414dd 100644 --- a/docs/menu.cue +++ b/docs/menu.cue @@ -858,6 +858,20 @@ text: "Environment Variables" path: "/ts/develop/env-vars" file: "ts/develop/env-vars" + }, { + kind: "accordion" + text: "Monorepo" + accordion: [{ + kind: "basic" + text: "Turborepo" + path: "/ts/develop/monorepo/turborepo" + file: "ts/develop/monorepo/turborepo" + }, { + kind: "basic" + text: "Nx" + path: "/ts/develop/monorepo/nx" + file: "ts/develop/monorepo/nx" + }] }] }, { From 8a57a4ef160fd545781fb4f59d63e8163b523092 Mon Sep 17 00:00:00 2001 From: Fredrik Enestad Date: Tue, 27 Jan 2026 09:24:45 +0100 Subject: [PATCH 4/6] callout for root path --- docs/ts/develop/monorepo/nx.md | 6 +++++- docs/ts/develop/monorepo/turborepo.md | 6 +++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/docs/ts/develop/monorepo/nx.md b/docs/ts/develop/monorepo/nx.md index 068ac7c00b..fc02b50bff 100644 --- a/docs/ts/develop/monorepo/nx.md +++ b/docs/ts/develop/monorepo/nx.md @@ -305,7 +305,11 @@ The `"dependsOn": ["^build"]` configuration ensures all dependencies are built b ### Deployment -When deploying via Encore Cloud or exporting a Docker image, the `prebuild` hook in `encore.app` automatically runs the Nx build. No additional setup is needed. +When deploying via Encore Cloud or exporting a Docker image, the `prebuild` hook in `encore.app` automatically runs the Nx build. + + +When deploying a monorepo to Encore Cloud, configure the root path to your Encore app in the app settings: **Settings > General > Root Directory** (e.g., `apps/backend`). + ## Key points diff --git a/docs/ts/develop/monorepo/turborepo.md b/docs/ts/develop/monorepo/turborepo.md index 4dc1cb1602..0453f5ed75 100644 --- a/docs/ts/develop/monorepo/turborepo.md +++ b/docs/ts/develop/monorepo/turborepo.md @@ -263,7 +263,11 @@ The `turbo.json` configuration ensures `@repo/shared` is built before the backen ### Deployment -When deploying via Encore Cloud or exporting a Docker image, the `prebuild` hook in `encore.app` automatically runs the Turborepo build pipeline. No additional setup is needed. +When deploying via Encore Cloud or exporting a Docker image, the `prebuild` hook in `encore.app` automatically runs the Turborepo build pipeline. + + +When deploying a monorepo to Encore Cloud, configure the root path to your Encore app in the app settings: **Settings > General > Root Directory** (e.g., `apps/backend`). + ## Key points From 05de083443b04bd97fd5ea984d3c82c016cd90c6 Mon Sep 17 00:00:00 2001 From: Fredrik Enestad Date: Tue, 27 Jan 2026 10:23:19 +0100 Subject: [PATCH 5/6] fix callout --- docs/ts/develop/monorepo/turborepo.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/ts/develop/monorepo/turborepo.md b/docs/ts/develop/monorepo/turborepo.md index 0453f5ed75..6fda8902a9 100644 --- a/docs/ts/develop/monorepo/turborepo.md +++ b/docs/ts/develop/monorepo/turborepo.md @@ -266,7 +266,9 @@ The `turbo.json` configuration ensures `@repo/shared` is built before the backen When deploying via Encore Cloud or exporting a Docker image, the `prebuild` hook in `encore.app` automatically runs the Turborepo build pipeline. + When deploying a monorepo to Encore Cloud, configure the root path to your Encore app in the app settings: **Settings > General > Root Directory** (e.g., `apps/backend`). + ## Key points From 61ca833cf676521a0c06b197941ee8c5f8f73773 Mon Sep 17 00:00:00 2001 From: Fredrik Enestad Date: Tue, 27 Jan 2026 10:26:04 +0100 Subject: [PATCH 6/6] clean up --- docs/ts/develop/monorepo/nx.md | 4 ++-- docs/ts/develop/monorepo/turborepo.md | 2 -- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/docs/ts/develop/monorepo/nx.md b/docs/ts/develop/monorepo/nx.md index fc02b50bff..ca6502cc7d 100644 --- a/docs/ts/develop/monorepo/nx.md +++ b/docs/ts/develop/monorepo/nx.md @@ -308,7 +308,9 @@ The `"dependsOn": ["^build"]` configuration ensures all dependencies are built b When deploying via Encore Cloud or exporting a Docker image, the `prebuild` hook in `encore.app` automatically runs the Nx build. + When deploying a monorepo to Encore Cloud, configure the root path to your Encore app in the app settings: **Settings > General > Root Directory** (e.g., `apps/backend`). + ## Key points @@ -316,5 +318,3 @@ When deploying a monorepo to Encore Cloud, configure the root path to your Encor - **Local development**: Run `npx nx build-deps @repo/backend` before `encore run`, or use `npx nx dev @repo/backend` to handle dependency ordering automatically - **Prebuild hook**: The `prebuild` hook in `encore.app` runs during deployment (Encore Cloud) or Docker export, not during local development - **Task dependencies**: Use `"dependsOn": ["^build"]` in `project.json` to automatically build all dependencies before running a target -- **ESM modules**: Use `"type": "module"` in package.json files for ES module support -- **Exports field**: Define the `exports` field in shared packages to specify entry points for both types and JavaScript diff --git a/docs/ts/develop/monorepo/turborepo.md b/docs/ts/develop/monorepo/turborepo.md index 6fda8902a9..842b47f051 100644 --- a/docs/ts/develop/monorepo/turborepo.md +++ b/docs/ts/develop/monorepo/turborepo.md @@ -276,5 +276,3 @@ When deploying a monorepo to Encore Cloud, configure the root path to your Encor - **Local development**: Run `npx turbo run build` before `encore run`, or use `npx turbo run dev --filter=@repo/backend` to handle dependency ordering automatically - **Prebuild hook**: The `prebuild` hook in `encore.app` runs during deployment (Encore Cloud) or Docker export, not during local development - **Turborepo filter**: Using `--filter=@repo/backend^...` builds only the dependencies of the backend (the `^` excludes the package itself) -- **ESM modules**: Use `"type": "module"` in package.json files for ES module support -- **Exports field**: Define the `exports` field in shared packages to specify entry points for both types and JavaScript