Skip to main content

Overview

LibreChat uses npm workspaces to manage its monorepo structure. The project is organized into multiple packages with clear boundaries and dependencies.
LibreChat/
├── api/                    # Legacy Express server (JavaScript)
├── client/                 # Frontend SPA (React + TypeScript)
├── packages/
   ├── api/               # New backend code (TypeScript only)
   ├── client/            # Shared frontend utilities
   ├── data-provider/     # Shared API contracts
   └── data-schemas/      # Database models
├── config/                # Configuration scripts
├── e2e/                   # Playwright E2E tests
├── package.json           # Root package with workspaces
└── turbo.json            # Turborepo configuration

Workspace Configuration

The root package.json defines the workspace structure:
package.json
{
  "name": "LibreChat",
  "version": "v0.8.3-rc1",
  "packageManager": "[email protected]",
  "workspaces": [
    "api",
    "client",
    "packages/*"
  ]
}
This configuration allows:
  • Shared dependency management across all packages
  • Hoisted dependencies to the root node_modules
  • Local package linking without publishing
  • Unified scripts and tooling

Workspaces

Backend Workspaces

Location: /apiPackage Name: @librechat/backendLanguage: JavaScript (legacy)Purpose: Express server - minimize changes hereDependencies:
{
  "@librechat/api": "*",
  "@librechat/data-schemas": "*",
  "librechat-data-provider": "*",
  "@librechat/agents": "^3.1.54"
}
Key Files:
  • server/index.js - Main entry point
  • server/routes/ - API route definitions
  • server/controllers/ - Request handlers
  • server/middleware/ - Express middleware
  • server/services/ - Business logic (migrating out)
  • models/ - Mongoose models (migrating out)
Development:
npm run backend:dev  # Start with nodemon
This is legacy code. All new backend features must be implemented in TypeScript in /packages/api.

Frontend Workspaces

Location: /clientPackage Name: @librechat/frontendLanguage: TypeScript + ReactPurpose: Main frontend SPADependencies:
{
  "@librechat/client": "*",
  "librechat-data-provider": "*",
  "react": "^18.2.0",
  "@tanstack/react-query": "^4.28.0"
}
Structure:
src/
├── components/       # React components by feature
├── data-provider/    # React Query hooks
├── hooks/           # Custom React hooks
├── routes/          # React Router routes
├── store/           # Recoil state atoms
├── locales/         # i18n translations (43 languages)
├── utils/           # Utility functions
├── App.jsx          # Root component
└── main.jsx         # Entry point
Build:
npm run build:client
cd client && npm run build
Development:
npm run frontend:dev  # Vite dev server on :3090
Build Output:
  • Static files in dist/
  • Served by Express in production
  • HMR in development via Vite

Shared Workspace

Location: /packages/data-providerPackage Name: librechat-data-providerLanguage: TypeScriptPurpose: Shared API types, endpoints, and data service used by both frontend and backendDependencies: None (foundation of dependency tree)Structure:
src/
├── api-endpoints.ts    # API endpoint URLs
├── data-service.ts     # HTTP client wrapper
├── keys.ts             # React Query keys
├── types/
   ├── queries.ts      # API request/response types
   ├── models.ts       # Domain model types
   └── index.ts
└── index.ts
Usage Example:
// In frontend
import { getConversations } from 'librechat-data-provider';
import { QueryKeys } from 'librechat-data-provider';

// In backend
import type { Conversation } from 'librechat-data-provider';
Build:
npm run build:data-provider
cd packages/data-provider && npm run build
After making changes to data-provider, you must rebuild it and restart both frontend and backend servers.

Dependency Graph

Key Points:
  • data-provider has no internal dependencies (foundation)
  • All packages depend on data-provider
  • /api (Express server) consumes /packages/api (TypeScript backend)
  • Frontend packages are independent of backend packages (except data-provider)

Workspace Boundaries

Critical Rules: Follow these boundaries to maintain clean architecture

Backend Rules

  1. All new backend code must be TypeScript in /packages/api
  2. Keep /api changes to the absolute minimum (thin JS wrappers calling into /packages/api)
  3. Database-specific shared logic goes in /packages/data-schemas
  4. Frontend/backend shared API logic (endpoints, types, data-service) goes in /packages/data-provider

Frontend Rules

  1. All user-facing text must use useLocalize()
  2. Only update English keys in client/src/locales/en/translation.json
  3. Feature hooks go in: client/src/data-provider/[Feature]/queries.ts[Feature]/index.tsclient/src/data-provider/index.ts
  4. React Query (@tanstack/react-query) for all API interactions
  5. QueryKeys and MutationKeys defined in packages/data-provider/src/keys.ts

Data Provider Integration

Endpoints: packages/data-provider/src/api-endpoints.ts
export const endpoints = {
  conversations: '/api/conversations',
  messages: '/api/messages',
  agents: '/api/agents',
};
Data Service: packages/data-provider/src/data-service.ts
export const dataService = {
  get: (url: string) => fetch(url).then(r => r.json()),
  post: (url: string, data: unknown) => fetch(url, { 
    method: 'POST', 
    body: JSON.stringify(data) 
  }),
};
Types: packages/data-provider/src/types/queries.ts
export interface ConversationListResponse {
  conversations: Conversation[];
  pageNumber: number;
  pageSize: number;
}

Development Workflow

Workflow for Backend Changes

1

Write TypeScript code

Add new features in /packages/api (TypeScript only):
// packages/api/src/services/agent-service.ts
export async function createAgent(data: AgentInput) {
  // Implementation
}
2

Build the package

npm run build:api
3

Create thin wrapper in /api

Add minimal JavaScript wrapper in /api:
// api/server/controllers/agents.js
const { createAgent } = require('@librechat/api');

async function createAgentController(req, res) {
  const result = await createAgent(req.body);
  res.json(result);
}
4

Restart backend

npm run backend:dev

Workflow for Data Provider Changes

1

Update data-provider

Modify types, endpoints, or data service:
// packages/data-provider/src/types/queries.ts
export interface NewFeatureRequest {
  name: string;
  config: Record<string, unknown>;
}
2

Rebuild data-provider

npm run build:data-provider
This is critical! Both frontend and backend depend on this package.
3

Update consumers

Update code in /api, /packages/api, or /client to use new types:
// client/src/data-provider/Feature/queries.ts
import type { NewFeatureRequest } from 'librechat-data-provider';
4

Restart servers

# Terminal 1: Restart backend
npm run backend:dev

# Terminal 2: Restart frontend  
npm run frontend:dev

Workflow for Frontend Changes

1

Add components and hooks

Create components in /client/src/components/:
// client/src/components/Features/NewFeature.tsx
export function NewFeature() {
  const localize = useLocalize();
  return <div>{localize('com_ui_new_feature')}</div>;
}
2

Add translations

Update only English translations:
// client/src/locales/en/translation.json
{
  "com_ui_new_feature": "New Feature"
}
3

Create data hooks

Add React Query hooks:
// client/src/data-provider/NewFeature/queries.ts
export function useNewFeature() {
  return useQuery(
    QueryKeys.newFeature,
    () => dataService.get('/api/new-feature')
  );
}
4

Hot reload

Vite will automatically reload. No restart needed!

Build Commands Reference

Root-Level Build Commands

# Build everything with Turborepo (parallel, cached)
npm run build

# Build all packages sequentially (legacy)
npm run frontend

# Build individual packages
npm run build:data-provider
npm run build:data-schemas
npm run build:api
npm run build:client-package
npm run build:client

# Build all packages (no client)
npm run build:packages

Workspace-Specific Builds

# Build from within a workspace
cd packages/api && npm run build
cd packages/data-provider && npm run build
cd client && npm run build

Turborepo Benefits

Parallel Execution

Builds multiple packages simultaneously when dependencies allow

Smart Caching

Only rebuilds packages that have changed since last build

Dependency Awareness

Automatically builds dependencies in correct order

Remote Caching

Can share build cache across team and CI/CD

Package Scripts

Shared Scripts (Root)

The root package.json provides scripts that work across all workspaces:
{
  "scripts": {
    "smart-reinstall": "node config/smart-reinstall.js",
    "reinstall": "node config/update.js -l -g",
    "backend": "cross-env NODE_ENV=production node api/server/index.js",
    "backend:dev": "cross-env NODE_ENV=development npx nodemon api/server/index.js",
    "frontend:dev": "cd client && npm run dev",
    "build": "npx turbo run build",
    "test:all": "npm run test:client && npm run test:api && npm run test:packages:api",
    "lint": "eslint \"{,!(node_modules|venv)/**/}*.{js,jsx,ts,tsx}\"",
    "lint:fix": "eslint --fix \"{,!(node_modules|venv)/**/}*.{js,jsx,ts,tsx}\"",
    "format": "npx prettier --write \"{,!(node_modules|venv)/**/}*.{js,jsx,ts,tsx}\""
  }
}

Testing Strategy

Per-Workspace Testing

Tests are run from their respective workspace directories:
# Run tests in a specific workspace
cd api && npx jest <pattern>
cd packages/api && npx jest <pattern>
cd client && npm run test

Root-Level Test Commands

# Run all tests
npm run test:all

# Run specific workspace tests
npm run test:client
npm run test:api
npm run test:packages:api
npm run test:packages:data-provider
npm run test:packages:data-schemas

Frontend Tests

  • Located in __tests__ directories alongside components
  • Use test/layout-test-utils for rendering
  • Cover loading, success, and error states
  • Mock data-provider hooks and external dependencies

Backend Tests

  • Framework: Jest
  • Integration tests with MongoDB Memory Server
  • Unit tests for services and utilities
  • Mock external API calls

External Dependencies

@librechat/agents

Source: External repository (/home/danny/agentus in dev environment) Version: ^3.1.54 Purpose: AI agent orchestration and tool execution Used by: /api and /packages/api Features:
  • LangChain integration
  • Custom tool definitions
  • Agent state management
  • Context protocol support
This is maintained by the same team but lives in a separate repository. It’s consumed as an npm dependency.

Common Patterns

Importing from Workspaces

// Import from data-provider (works in any workspace)
import { QueryKeys } from 'librechat-data-provider';
import type { Conversation } from 'librechat-data-provider';

// Import from packages/api (only in /api)
const { createAgent } = require('@librechat/api');

// Import from packages/client (only in /client)
import { useTheme } from '@librechat/client';

// Import from packages/data-schemas (in backend only)
import { UserSchema } from '@librechat/data-schemas';

Module Resolution

Workspace packages are linked locally during development:
// In client/package.json
{
  "dependencies": {
    "librechat-data-provider": "*"  // Links to packages/data-provider
  }
}
The * version means “use the local workspace version”.

Next Steps

Setup Guide

Get your development environment running

Architecture

Understand how components interact

Code Style

Follow LibreChat coding standards

Frontend Guide

Build React components

Build docs developers (and LLMs) love