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:
{
"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
api (Legacy)
packages/api
packages/data-schemas
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.
Location : /packages/apiPackage Name : @librechat/apiLanguage : TypeScript (strict mode)Purpose : All new backend code lives hereDependencies :{
"@librechat/data-schemas" : "*" ,
"librechat-data-provider" : "*"
}
Peer Dependencies : Major libraries like @librechat/agents, mongoose, express, etc.Build Configuration :
Tool : Rollup
Output : CommonJS + ES modules
Entry : src/index.ts
Types : dist/types/index.d.ts
Key Features :
MCP (Model Context Protocol) services
Cache management
Stream handling
File processing
Agent orchestration
Build :npm run build:api
cd packages/api && npm run build
This package is consumed by /api as a dependency. The legacy server calls into these TypeScript modules.
Location : /packages/data-schemasPackage Name : @librechat/data-schemasLanguage : TypeScriptPurpose : Database models and schemas shareable across backend projectsDependencies :{
"librechat-data-provider" : "*"
}
Contains :
Mongoose schema definitions
Database model types
Zod validation schemas
Migration utilities
Example Structure :src /
├── schemas /
│ ├── user . ts
│ ├── conversation . ts
│ ├── message . ts
│ └── agent . ts
├── models /
└── index . ts
Build :npm run build:data-schemas
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
Location : /packages/clientPackage Name : @librechat/clientLanguage : TypeScriptPurpose : Shared frontend utilities and componentsDependencies :{
"librechat-data-provider" : "*"
}
Contains :
Reusable UI components
Theme configuration
Common hooks
Shared TypeScript types
Utility functions
Example Exports :// Shared types
export * from './types' ;
// Common hooks
export { useTheme } from './hooks' ;
// Utility functions
export * from './utils' ;
Build :npm run build:client-package
Shared Workspace
packages/data-provider - The Foundation
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
All new backend code must be TypeScript in /packages/api
Keep /api changes to the absolute minimum (thin JS wrappers calling into /packages/api)
Database-specific shared logic goes in /packages/data-schemas
Frontend/backend shared API logic (endpoints, types, data-service) goes in /packages/data-provider
Frontend Rules
All user-facing text must use useLocalize()
Only update English keys in client/src/locales/en/translation.json
Feature hooks go in: client/src/data-provider/[Feature]/queries.ts → [Feature]/index.ts → client/src/data-provider/index.ts
React Query (@tanstack/react-query) for all API interactions
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
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
}
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 );
}
Workflow for Data Provider Changes
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 >;
}
Rebuild data-provider
npm run build:data-provider
This is critical! Both frontend and backend depend on this package.
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' ;
Restart servers
# Terminal 1: Restart backend
npm run backend:dev
# Terminal 2: Restart frontend
npm run frontend:dev
Workflow for Frontend Changes
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 > ;
}
Add translations
Update only English translations: // client/src/locales/en/translation.json
{
"com_ui_new_feature" : "New Feature"
}
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' )
);
}
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 < patter n >
cd packages/api && npx jest < patter n >
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