How Dify Chat Uses a PNPM Workspace to Structure Its Monorepo

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 at the repository root. This file declares the workspace glob pattern and version catalog:

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 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, TypeScript configuration, and RSLib build configuration (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 declares:

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

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

// 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 defines a version catalog that centralizes dependency versions across all packages. Rather than hardcoding version numbers in individual package.json files, packages reference the catalog using the catalog: protocol:


# pnpm-workspace.yaml

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

In a package's package.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:

pnpm --filter dify-chat-platform dev

This command resolves the workspace package named dify-chat-platform (defined in 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 provides recursive scripts using the -r flag:


# 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 within each package. TypeScript configurations extend a root 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 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 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 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 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.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →