# How Dify Chat Uses a PNPM Workspace to Structure Its Monorepo

> Discover how Dify Chat structures its monorepo with a PNPM workspace. Learn about package linking, versioning, and selective builds for efficient development.

- Repository: [lexmin0412/dify-chat](https://github.com/lexmin0412/dify-chat)
- Tags: internals
- Published: 2026-03-06

---

**Dify Chat organizes its monorepo using a PNPM workspace that links seven packages via `workspace:^` specifiers, enforces consistent versioning through a central catalog, and enables selective builds with `pnpm --filter`.**

The `lexmin0412/dify-chat` repository demonstrates a production-grade **pnpm workspace** setup that manages multiple interconnected packages under a unified build system. By leveraging PNPM's workspace protocol and version catalog, the monorepo achieves deterministic dependency resolution across seven distinct packages while maintaining fast, selective development workflows.

## Workspace Configuration and Layout

### Centralized Workspace Definition

The foundation of the monorepo resides in [`pnpm-workspace.yaml`](https://github.com/lexmin0412/dify-chat/blob/main/pnpm-workspace.yaml) at the repository root. This file declares the workspace glob pattern and version catalog:

```yaml
packages:
  - 'packages/*'

```

The `packages/*` glob automatically includes every subdirectory under `packages/` as a workspace member. This configuration eliminates the need to manually list each package, allowing new packages to be recognized immediately upon creation.

### Root Package Orchestration

The root [`package.json`](https://github.com/lexmin0412/dify-chat/blob/main/package.json) defines repository-wide scripts that leverage PNPM's recursive execution capabilities. Key scripts include `pnpm -r build` to compile all packages and `pnpm --filter @dify-chat/* build` to target specific scopes. This root configuration acts as the command center for the entire monorepo, delegating tasks to individual packages while maintaining a unified interface.

## Package Architecture and Dependencies

### The Seven Packages

Dify Chat organizes its functionality into seven logical packages under the `packages/` directory:

- **`api`** – Dify API client that consumes core utilities and helpers
- **`core`** – Core abstractions and shared utilities consumed by most other packages
- **`helpers`** – Shared helper functions available across the monorepo
- **`components`** – UI component library (deprecated in favor of platform-specific implementations)
- **`theme`** – Theme selector and Tailwind CSS configuration
- **`docs`** – Rspress documentation site
- **`platform`** – Next.js 15 App Router application (backend and admin UI)
- **`react-app`** – Stand-alone React front-end application

Each package maintains its own [`package.json`](https://github.com/lexmin0412/dify-chat/blob/main/package.json), TypeScript configuration, and RSLib build configuration ([`rslib.config.ts`](https://github.com/lexmin0412/dify-chat/blob/main/rslib.config.ts)), enabling independent versioning while sharing build tooling.

### Internal Linking with workspace:^

Packages reference one another using the `workspace:^` protocol, which instructs PNPM to resolve dependencies from the local workspace rather than the npm registry. For example, [`packages/api/package.json`](https://github.com/lexmin0412/dify-chat/blob/main/packages/api/package.json) declares:

```json
{
  "dependencies": {
    "@dify-chat/core": "workspace:^",
    "@dify-chat/helpers": "workspace:^"
  }
}

```

This configuration enables the API package to import utilities directly from sibling packages:

```typescript
// packages/api/src/index.ts
import { debounce } from '@dify-chat/helpers';
import { createClient } from '@dify-chat/core';

export const difyClient = createClient({ debounce });

```

The `workspace:^` specifier ensures that changes to `@dify-chat/core` or `@dify-chat/helpers` are immediately reflected in the API package without requiring a publish cycle.

### Version Catalog for Consistency

The [`pnpm-workspace.yaml`](https://github.com/lexmin0412/dify-chat/blob/main/pnpm-workspace.yaml) defines a **version catalog** that centralizes dependency versions across all packages. Rather than hardcoding version numbers in individual [`package.json`](https://github.com/lexmin0412/dify-chat/blob/main/package.json) files, packages reference the catalog using the `catalog:` protocol:

```yaml

# pnpm-workspace.yaml

catalog:
  react: ^18.2.0
  typescript: ^5.3.0
  tailwindcss: ^3.4.0

```

In a package's [`package.json`](https://github.com/lexmin0412/dify-chat/blob/main/package.json):

```json
{
  "dependencies": {
    "react": "catalog:",
    "typescript": "catalog:"
  }
}

```

This approach guarantees that every package uses identical versions of React, TypeScript, and other shared dependencies, eliminating version drift and "works on my machine" issues.

## Development Workflow and Build Orchestration

### Selective Builds with --filter

PNPM's `--filter` flag enables developers to build or serve individual packages without compiling the entire monorepo. To start only the Next.js platform application:

```bash
pnpm --filter dify-chat-platform dev

```

This command resolves the workspace package named `dify-chat-platform` (defined in [`packages/platform/package.json`](https://github.com/lexmin0412/dify-chat/blob/main/packages/platform/package.json)) and executes its `dev` script, ignoring other packages. This selective approach reduces startup time from minutes to seconds during local development.

### Recursive Operations

For tasks that must run across all packages, the root [`package.json`](https://github.com/lexmin0412/dify-chat/blob/main/package.json) provides recursive scripts using the `-r` flag:

```bash

# Build every package in dependency order

pnpm -r build

# Run tests across the entire monorepo

pnpm test

```

The `-r` (recursive) flag instructs PNPM to execute the specified script in every workspace package, respecting topological order for builds (dependencies build before dependents).

### Shared Tooling and RSLib

Each package inherits shared tooling configurations while maintaining independent build outputs. The monorepo uses **RSLib** (Rspack's library build tool) for package compilation, configured in [`rslib.config.ts`](https://github.com/lexmin0412/dify-chat/blob/main/rslib.config.ts) within each package. TypeScript configurations extend a root [`tsconfig.json`](https://github.com/lexmin0412/dify-chat/blob/main/tsconfig.json), ensuring consistent compiler options while allowing package-specific overrides.

This shared tooling approach means that adding a new package requires only minimal configuration—copying the standard [`rslib.config.ts`](https://github.com/lexmin0412/dify-chat/blob/main/rslib.config.ts) and extending the root TypeScript config—while immediately gaining access to the monorepo's build, lint, and test infrastructure.

## Summary

- **Dify Chat** uses a PNPM workspace to manage seven interconnected packages under a unified build system.
- The **[`pnpm-workspace.yaml`](https://github.com/lexmin0412/dify-chat/blob/main/pnpm-workspace.yaml)** defines package globs and a central version catalog that ensures consistent dependency versions across all packages.
- **Internal dependencies** use the `workspace:^` protocol, enabling packages to import from one another without publishing to npm.
- **Selective builds** via `pnpm --filter` allow developers to work on individual packages, while `pnpm -r` executes commands across the entire monorepo.
- **Shared tooling** configurations for TypeScript and RSLib reduce boilerplate when creating new packages.

## Frequently Asked Questions

### What is a pnpm workspace?

A pnpm workspace is a monorepo management feature that links multiple packages within a single repository, allowing them to depend on one another using the `workspace:` protocol. In Dify Chat, this enables the `api` package to import utilities from `core` and `helpers` as if they were published npm packages, while actually resolving to the local source code.

### How does Dify Chat handle versioning across packages?

Dify Chat uses the **catalog** feature in [`pnpm-workspace.yaml`](https://github.com/lexmin0412/dify-chat/blob/main/pnpm-workspace.yaml) to define shared dependency versions once and reference them via `catalog:` in individual package.json files. This ensures that every package uses identical versions of React, TypeScript, and Tailwind CSS, preventing version mismatches that could cause runtime errors.

### Can I build only one package in the Dify Chat monorepo?

Yes. Using PNPM's `--filter` flag, you can target specific packages without building the entire monorepo. For example, `pnpm --filter dify-chat-platform dev` starts only the Next.js platform application, while `pnpm --filter @dify-chat/api build` compiles only the API package and its dependencies.

### How are dependencies shared between packages?

Dependencies are shared through PNPM's content-addressable store and workspace linking. When [`packages/api/package.json`](https://github.com/lexmin0412/dify-chat/blob/main/packages/api/package.json) declares `"@dify-chat/core": "workspace:^"`, PNPM creates a symlink to the `core` package's build output. This allows code like `import { createClient } from '@dify-chat/core'` to resolve instantly to the local source, with changes reflected immediately without republishing.