LibreChat

Code Standards and Conventions

Coding standards, workspace boundaries, and conventions for contributing to LibreChat.

Workspace Boundaries

LibreChat is a monorepo. All new code should target the correct workspace:

WorkspaceLanguageSidePurpose
/apiJS (legacy)BackendExpress server — minimize changes here
/packages/apiTypeScriptBackendNew backend code lives here (TS only, consumed by /api)
/packages/data-schemasTypeScriptBackendDatabase models/schemas
/packages/data-providerTypeScriptSharedAPI types, endpoints, data-service — used by frontend and backend
/clientTypeScript/ReactFrontendFrontend SPA
/packages/clientTypeScriptFrontendShared frontend utilities
  • 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.
  • Build all compiled code from project root: npm run build.

General Guidelines

  • Use "clean code" principles: keep functions and modules small, adhere to the single responsibility principle, and write expressive and readable code.
  • Use meaningful and descriptive variable and function names.
  • Prioritize code readability and maintainability over brevity.
  • Use the provided .eslintrc and .prettierrc files for consistent code formatting.
  • Fix all formatting lint errors using auto-fix when available. All TypeScript/ESLint warnings and errors must be resolved.

Code Structure

  • Never-nesting: use early returns, flat code, minimal indentation. Break complex operations into well-named helpers.
  • Functional first: pure functions, immutable data, map/filter/reduce over imperative loops. Only reach for OOP when it clearly improves domain modeling or state encapsulation.
  • No dynamic imports unless absolutely necessary.
  • Extract repeated logic into dedicated utility functions (DRY).

Iteration and Performance

  • Minimize looping — especially over shared data structures like message arrays, which are iterated frequently. Every additional pass adds up at scale.
  • Consolidate sequential O(n) operations into a single pass whenever possible; never loop over the same collection twice if the work can be combined.
  • Choose data structures that reduce the need to iterate (e.g., Map/Set for lookups instead of Array.find/Array.includes).
  • Avoid unnecessary object creation; consider space-time tradeoffs.
  • Prevent memory leaks: be careful with closures, dispose resources/event listeners, avoid circular references.

Type Safety

  • Never use any. Explicit types for all parameters, return values, and variables.
  • Limit unknown — avoid unknown, Record<string, unknown>, and as unknown as T assertions. A Record<string, unknown> almost always signals a missing explicit type definition.
  • Don't duplicate types — check whether a type already exists in the project (especially packages/data-provider) before defining a new one. Reuse and extend existing types.
  • Use union types, generics, and interfaces appropriately.

Comments and Documentation

  • Write self-documenting code; no inline comments narrating what code does.
  • JSDoc only for complex/non-obvious logic or intellisense on public APIs.
  • Single-line JSDoc for brief docs, multi-line for complex cases.
  • Avoid standalone // comments unless absolutely necessary.

Import Order

Imports are organized into three sections (in order):

  1. Package imports — sorted from shortest to longest line length (react is always the first import).
  2. import type imports — sorted from longest to shortest (package types first, then local types; length sorting resets between sub-groups).
  3. Local/project imports — sorted from longest to shortest.
  • Consolidate value imports from the same module as much as possible.
  • Always use standalone import type { ... } for type imports; never use inline type keyword inside value imports (e.g., import { Foo, type Bar } is wrong).

Loop Preferences

  • Limit looping as much as possible. Prefer single-pass transformations and avoid re-iterating the same data.
  • for (let i = 0; ...) for performance-critical or index-dependent operations.
  • for...of for simple array iteration.
  • for...in only for object property enumeration.

Node.js API Server

API Design

  • Follow RESTful principles when designing APIs.
  • Use meaningful and descriptive names for routes, controllers, services, and models.
  • Use appropriate HTTP methods (GET, POST, PUT, DELETE) for each route.
  • Use proper status codes and response structures for consistent API responses (2xx for success, 4xx for bad request from client, 5xx for server error).
  • Use try-catch blocks to catch and handle exceptions gracefully.
  • Implement proper error handling and consistently return appropriate error responses.
  • Use the logging system included in the utils directory to log important events and errors.
  • Do JWT-based, stateless authentication using the requireJWTAuth middleware.

File Structure

New backend code goes in /packages/api as TypeScript. The legacy /api directory follows this structure:

Routes

Specifies each HTTP request method, any middleware to be used, and the controller function to be called for each route.

  • Define routes using the Express Router in separate files for each resource or logical grouping.
  • Use descriptive route names and adhere to RESTful conventions.
  • Keep routes concise and focused on a single responsibility.
  • Prefix all routes with the /api namespace.

Controllers

Contains the logic for each route, including calling the appropriate service functions and returning the appropriate response status code and JSON body.

  • Create a separate controller file for each route to handle the request/response logic.
  • Name controller files using the PascalCase convention and append "Controller" to the file name (e.g., UserController.js).
  • Keep controllers thin by delegating complex operations to service or model files.

Services

Contains complex business logic or operations shared across multiple controllers.

  • Name service files using the PascalCase convention and append "Service" to the file name (e.g., AuthService.js).
  • Avoid tightly coupling services to specific models or databases for better reusability.
  • Maintain a single responsibility principle within each service.

Models

Defines Mongoose models to represent data entities and their relationships.

  • Use singular, PascalCase names for model files and their associated collections (e.g., User.js and users collection).
  • Include only the necessary fields, indexes, and validations in the models.
  • Keep models independent of the API layer by avoiding direct references to request/response objects.

Database Access (MongoDB and Mongoose)

  • Use Mongoose (https://mongoosejs.com) as the MongoDB ODM.
  • Create separate model files for each entity and ensure clear separation of concerns.
  • Use Mongoose schema validation to enforce data integrity.
  • Handle database connections efficiently and avoid connection leaks.
  • Use Mongoose query builders to create concise and readable database queries.

React Client

General TypeScript and React Best Practices

  • Use TypeScript best practices to benefit from static typing and improved tooling.
  • Group related files together within feature directories (e.g., SidePanel/Memories/).
  • Name components using the PascalCase convention.
  • Use concise and descriptive names that accurately reflect the component's purpose.
  • Split complex components into smaller, reusable ones when appropriate.
  • Keep the rendering logic within components minimal.
  • Extract reusable parts into separate functions or hooks.
  • Apply prop type definitions using TypeScript types or interfaces.
  • Use form validation where appropriate (we use React Hook Form for form validation and submission).

Localization

  • All client-facing text must be localized using the useLocalize() hook.
  • Only update English keys in client/src/locales/en/translation.json (other languages are automated externally).
  • Use semantic localization key prefixes: com_ui_, com_assistants_, etc.
  • Always provide meaningful fallback text for new localization keys.

Data Services

  • Create data provider hooks in client/src/data-provider/[Feature]/queries.ts.
  • Export all hooks from client/src/data-provider/[Feature]/index.ts.
  • Add feature exports to main client/src/data-provider/index.ts.
  • Use React Query (@tanstack/react-query) for all API interactions.
  • Implement proper query invalidation on mutations.
  • Add QueryKeys and MutationKeys to packages/data-provider/src/keys.ts.

When adding shared API integration, update:

  • packages/data-provider/src/api-endpoints.ts (endpoints)
  • packages/data-provider/src/data-service.ts (data service functions)
  • packages/data-provider/src/types/queries.ts (TypeScript types)

Performance

  • Prioritize memory and speed efficiency at scale.
  • Implement proper cursor pagination for large datasets.
  • Avoid unnecessary re-renders with proper dependency arrays.
  • Leverage React Query's caching and background refetching features.

Testing and Documentation

  • Write unit tests for all critical and complex functionalities using Jest.
  • Write integration tests for all API endpoints using Supertest.
  • Write end-to-end tests for all client-side functionalities using Playwright.
  • Use descriptive test case and function names to clearly express the test's purpose.
  • Run tests from their workspace directory: cd api && npx jest <pattern>, cd packages/api && npx jest <pattern>, etc.
  • Cover loading, success, and error states for UI/data flows.
  • Use test/layout-test-utils for rendering components in frontend tests.

How is this guide?