Skip to main content
LibreChat is joining ClickHouse to power the open-source Agentic Data Stack 🎉 Learn more
LibreChat

Code-Standards und Konventionen

Codierungsstandards, Arbeitsbereichsgrenzen und Konventionen für die Mitarbeit an LibreChat.

Arbeitsbereich-Grenzen

LibreChat ist ein Monorepo. Jeder neue Code sollte auf den korrekten Workspace ausgerichtet sein:

ArbeitsbereichSpracheSeiteZweck
/apiJS (Legacy)BackendExpress-Server — Änderungen hier minimieren
/packages/apiTypeScriptBackendNeuer Backend-Code befindet sich hier (nur TS, wird von /api konsumiert)
/packages/data-schemasTypeScriptBackendDatenbankmodelle/-schemas und datenbankspezifische geteilte Logik
/packages/data-providerTypeScriptGeteiltAPI-Typen, Endpoints, Data-Service — wird von Frontend und Backend verwendet
/clientTypeScript/ReactFrontendFrontend SPA
/packages/clientTypeScriptFrontendGeteilte Frontend-Dienstprogramme
  • All new backend code must be TypeScript in /packages/api.
  • Halten Sie Änderungen an /api auf ein absolutes Minimum (schlanke JS-Wrapper, die /packages/api aufrufen).
  • Datenbankspezifische gemeinsame Logik gehört in /packages/data-schemas.
  • Frontend/Backend-geteilte API-Logik (endpoints, types, data-service) befindet sich in /packages/data-provider.
  • Erstellen Sie den gesamten kompilierten Code vom Projektstammverzeichnis aus: npm run build.
  • Erstellen Sie den gemeinsam genutzten data-provider-Code nach API-/Typänderungen neu: npm run build:data-provider.

Allgemeine Richtlinien

  • Verwenden Sie „Clean Code“-Prinzipien: Halten Sie Funktionen und Module klein, befolgen Sie das Single-Responsibility-Prinzip und schreiben Sie ausdrucksstarken sowie lesbaren Code.
  • Verwenden Sie aussagekräftige und beschreibende Variablen- und Funktionsnamen.
  • Priorisieren Sie die Lesbarkeit und Wartbarkeit von Code gegenüber Kürze.
  • Verwenden Sie die bereitgestellten .eslintrc- und .prettierrc-Dateien für eine konsistente Code-Formatierung.
  • Beheben Sie alle Formatierungs-Lint-Fehler mithilfe der Auto-Fix-Funktion, sofern verfügbar. Alle TypeScript/ESLint-Warnungen und -Fehler müssen behoben werden.

Benennung und Dateiorganisation

  • Verwenden Sie nach Möglichkeit Dateinamen, die aus einem einzigen Wort bestehen, wie permissions.ts, capabilities.ts oder service.ts.
  • Wenn mehrere Wörter benötigt werden, bevorzugen Sie ein einzelnes Verzeichnis, das den Kontext der Datei angibt, wie zum Beispiel admin/capabilities.ts anstelle von adminCapabilities.ts.
  • Lassen Sie das Verzeichnis den Kontext liefern. Bevorzugen Sie app/service.ts gegenüber app/appConfigService.ts.

Code-Struktur

  • Never-nesting: Verwenden Sie Early Returns, flachen Code und minimale Einrückungen. Unterteilen Sie komplexe Operationen in gut benannte Hilfsfunktionen.
  • Funktional zuerst: reine Funktionen, unveränderliche Daten, map/filter/reduce anstelle von imperativen Schleifen. Greifen Sie nur dann auf OOP zurück, wenn es die Domänenmodellierung oder die Kapselung von Zuständen eindeutig verbessert.
  • Keine dynamischen Importe, es sei denn, dies ist unbedingt erforderlich.
  • Extrahiere wiederkehrende Logik in dedizierte Hilfsfunktionen (DRY). Bevorzuge parametrisierte Helfer, Konstanten, gemeinsame Validatoren, zentralisierte Fehlerbehandlung und gemeinsame Typen gegenüber nahezu identischen Implementierungen.

Iteration und Performance

  • Schleifen minimieren — insbesondere bei gemeinsam genutzten Datenstrukturen wie Nachrichten-Arrays, die häufig durchlaufen werden. Jeder zusätzliche Durchlauf summiert sich bei zunehmender Skalierung.
  • Fassen Sie aufeinanderfolgende O(n)-Operationen wann immer möglich in einem einzigen Durchlauf zusammen; iterieren Sie niemals zweimal über dieselbe Sammlung, wenn die Arbeit kombiniert werden kann.
  • Wählen Sie Datenstrukturen, die die Notwendigkeit von Iterationen reduzieren (z. B. Map/Set für Suchvorgänge anstelle von Array.find/Array.includes).
  • Vermeiden Sie unnötige Objekterstellung; berücksichtigen Sie Zeit-Raum-Abwägungen.
  • Speicherlecks verhindern: Gehen Sie vorsichtig mit Closures um, geben Sie Ressourcen/Event-Listener frei und vermeiden Sie zirkuläre Referenzen.

Typsicherheit

  • Verwenden Sie niemals any. Explizite Typen für alle Parameter, Rückgabewerte und Variablen.
  • unknown einschränken — vermeiden Sie unknown, Record<string, unknown> und as unknown as T-Assertionen. Ein Record<string, unknown> deutet fast immer auf eine fehlende explizite Typdefinition hin.
  • Typen nicht duplizieren — prüfen Sie, ob ein Typ bereits im Projekt existiert (insbesondere in packages/data-provider), bevor Sie einen neuen definieren. Verwenden Sie vorhandene Typen wieder und erweitern Sie diese.
  • Verwenden Sie Union-Types, Generics und Interfaces auf angemessene Weise.

Kommentare und Dokumentation

  • Schreiben Sie selbstdokumentierenden Code; keine Inline-Kommentare, die beschreiben, was der Code tut.
  • JSDoc nur für komplexe/nicht offensichtliche Logik oder Intellisense bei öffentlichen APIs.
  • Einzeiliges JSDoc für kurze Dokumentationen, mehrzeiliges für komplexe Fälle.
  • Vermeiden Sie eigenständige // Kommentare, sofern dies nicht unbedingt erforderlich ist.

Import-Reihenfolge

Importe sind in drei Abschnitte unterteilt (in dieser Reihenfolge):

  1. Paketimporte — sortiert nach Zeilenlänge von kurz nach lang (react ist immer der erste Import).
  2. import type imports — sortiert von der längsten zur kürzesten (zuerst Paket-Typen, dann lokale Typen; die Längensortierung wird zwischen den Untergruppen zurückgesetzt).
  3. Lokale/Projekt-Importe — sortiert von der längsten zur kürzesten.
  • Fassen Sie Wertimporte aus demselben Modul so weit wie möglich zusammen.
  • Verwenden Sie immer eigenständige import type { ... } für Typ-Importe; verwenden Sie niemals das Inline-Schlüsselwort type innerhalb von Wert-Importen (z. B. ist import { Foo, type Bar } falsch).

Loop-Einstellungen

  • Begrenzen Sie Schleifen so weit wie möglich. Bevorzugen Sie Transformationen in einem einzigen Durchlauf und vermeiden Sie es, dieselben Daten mehrfach zu durchlaufen.
  • for (let i = 0; ...) für leistungsrelevante oder indexabhängige Operationen.
  • for...of für einfache Array-Iteration.
  • for...in nur für die Aufzählung von Objekteigenschaften.

Node.js API-Server

API-Design

  • Befolgen Sie RESTful-Prinzipien beim Entwurf von APIs.
  • Verwenden Sie aussagekräftige und beschreibende Namen für Routen, Controller, Services und Modelle.
  • Verwenden Sie für jede Route die entsprechenden HTTP-Methoden (GET, POST, PUT, DELETE).
  • Verwenden Sie korrekte Statuscodes und Antwortstrukturen für konsistente API-Antworten (2xx für Erfolg, 4xx für fehlerhafte Anfragen des Clients, 5xx für Serverfehler).
  • Verwenden Sie try-catch-Blöcke, um Exceptions abzufangen und ordnungsgemäß zu behandeln.
  • Implementieren Sie eine ordnungsgemäße Fehlerbehandlung und geben Sie konsistent geeignete Fehlerantworten zurück.
  • Verwenden Sie das im utils-Verzeichnis enthaltene Protokollierungssystem, um wichtige Ereignisse und Fehler zu protokollieren.
  • Führen Sie eine JWT-basierte, zustandslose Authentifizierung unter Verwendung der requireJWTAuth-Middleware durch.

Dateistruktur

Neuer Backend-Code kommt als TypeScript in /packages/api. Das Legacy-Verzeichnis /api folgt dieser Struktur:

Routen

Gibt jede HTTP-Anfragemethode, die zu verwendende Middleware sowie die für jede Route aufzurufende Controller-Funktion an.

  • Definieren Sie Routen mithilfe des Express Routers in separaten Dateien für jede Ressource oder logische Gruppierung.
  • Verwenden Sie beschreibende Routennamen und halten Sie sich an RESTful-Konventionen.
  • Halte Routen prägnant und auf eine einzige Verantwortung fokussiert.
  • Stellen Sie allen Routen den /api Namespace voran.

Controller

Enthält die Logik für jede Route, einschließlich des Aufrufs der entsprechenden Service-Funktionen und der Rückgabe des passenden Antwort-Statuscodes sowie des JSON-Bodys.

  • Erstellen Sie für jede Route eine separate Controller-Datei, um die Request/Response-Logik zu verarbeiten.
  • Benennen Sie Controller-Dateien nach der PascalCase-Konvention und hängen Sie „Controller“ an den Dateinamen an (z. B. UserController.js).
  • Halten Sie Controller schlank, indem Sie komplexe Vorgänge an Service- oder Model-Dateien delegieren.

Dienste

Enthält komplexe Geschäftslogik oder Vorgänge, die von mehreren Controllern gemeinsam genutzt werden.

  • Benennen Sie Service-Dateien nach der PascalCase-Konvention und hängen Sie „Service“ an den Dateinamen an (z. B. AuthService.js).
  • Vermeiden Sie eine enge Kopplung von Diensten an bestimmte Modelle oder Datenbanken, um die Wiederverwendbarkeit zu verbessern.
  • Behalten Sie das Prinzip der einzigen Verantwortlichkeit (Single Responsibility Principle) innerhalb jedes Dienstes bei.

Modelle

Definiert Mongoose-Modelle zur Darstellung von Datenentitäten und deren Beziehungen.

  • Verwenden Sie für Modelldateien und die zugehörigen Collections Namen im Singular und in PascalCase (z. B. User.js und users-Collection).
  • Fügen Sie nur die notwendigen Felder, Indizes und Validierungen in die Modelle ein.
  • Halten Sie Modelle unabhängig von der API-Schicht, indem Sie direkte Referenzen auf request/response-Objekte vermeiden.

Datenbankzugriff (MongoDB und Mongoose)

  • Verwenden Sie Mongoose (https://mongoosejs.com) als MongoDB ODM.
  • Erstellen Sie separate Model-Dateien für jede Entität und stellen Sie eine klare Trennung der Zuständigkeiten sicher.
  • Verwenden Sie die Mongoose-Schema-Validierung, um die Datenintegrität durchzusetzen.
  • Gehen Sie effizient mit Datenbankverbindungen um und vermeiden Sie Verbindungslecks.
  • Verwenden Sie Mongoose Query Builder, um prägnante und lesbare Datenbankabfragen zu erstellen.

React Client

Allgemeine Best Practices für TypeScript und React

  • Nutzen Sie TypeScript best practices, um von statischer Typisierung und verbesserten Werkzeugen zu profitieren.
  • Gruppieren Sie zusammengehörige Dateien innerhalb von Feature-Verzeichnissen (z. B. SidePanel/Memories/).
  • Benennen Sie Komponenten unter Verwendung der PascalCase-Konvention.
  • Verwenden Sie prägnante und beschreibende Namen, die den Zweck der Komponente genau widerspiegeln.
  • Teilen Sie komplexe Komponenten bei Bedarf in kleinere, wiederverwendbare Komponenten auf.
  • Halte die Rendering-Logik innerhalb von Komponenten minimal.
  • Extrahiere wiederverwendbare Teile in separate Funktionen oder Hooks.
  • Wenden Sie Prop-Typ-Definitionen mithilfe von TypeScript-Typen oder -Interfaces an.
  • Verwenden Sie bei Bedarf eine Formularvalidierung (wir verwenden React Hook Form für die Formularvalidierung und -übermittlung).

Lokalisierung

  • Alle für den Client sichtbaren Texte müssen mithilfe des useLocalize()-Hooks lokalisiert werden.
  • Aktualisiere nur die englischen Schlüssel in client/src/locales/en/translation.json (andere Sprachen werden extern automatisiert).
  • Verwenden Sie semantische Lokalisierungsschlüssel-Präfixe: com_ui_, com_assistants_ usw.
  • Stellen Sie immer aussagekräftigen Fallback-Text für neue Lokalisierungsschlüssel bereit.

Datendienste

  • Erstellen Sie Data-Provider-Hooks in client/src/data-provider/[Feature]/queries.ts.
  • Exportieren Sie alle Hooks aus client/src/data-provider/[Feature]/index.ts.
  • Füge Feature-Exporte zur Hauptdatei client/src/data-provider/index.ts hinzu.
  • Verwenden Sie React Query (@tanstack/react-query) für alle API-Interaktionen.
  • Implementieren Sie eine ordnungsgemäße Abfragevalidierung (Query Invalidation) bei Mutationen.
  • Füge QueryKeys und MutationKeys zu packages/data-provider/src/keys.ts hinzu.

Beim Hinzufügen einer gemeinsamen API-Integration aktualisieren Sie:

  • packages/data-provider/src/api-endpoints.ts (endpoints)
  • packages/data-provider/src/data-service.ts (Data-Service-Funktionen)
  • packages/data-provider/src/types/queries.ts (TypeScript-Typen)

Leistung

  • Priorisieren Sie Speichereffizienz und Geschwindigkeit bei Skalierung.
  • Implementieren Sie eine ordnungsgemäße Cursor-Paginierung für große Datensätze.
  • Vermeiden Sie unnötige Re-Renders durch korrekte Dependency-Arrays.
  • Nutzen Sie die Caching- und Hintergrund-Refetching-Funktionen von React Query.

Testen und Dokumentation

  • Schreiben Sie Unit-Tests für alle kritischen und komplexen Funktionalitäten mit Jest.
  • Schreibe Integrationstests für alle API-endpoints unter Verwendung von Supertest.
  • Schreiben Sie End-to-End-Tests für alle clientseitigen Funktionen mit Playwright.
  • Verwenden Sie beschreibende Testfall- und Funktionsnamen, um den Zweck des Tests klar auszudrücken.
  • Führen Sie Tests aus ihrem Arbeitsverzeichnis aus: cd api && npx jest <pattern>, cd packages/api && npx jest <pattern> usw.
  • Decken Sie Lade-, Erfolgs- und Fehlerzustände für UI-/Datenflüsse ab.
  • Verwenden Sie test/layout-test-utils für das Rendern von Komponenten in Frontend-Tests.
  • Bevorzugen Sie echte Logik gegenüber Mocks. Mocken Sie nur das, was nicht lokal kontrolliert werden kann, wie externe HTTP-APIs, ratenbegrenzte Dienste und nicht-deterministische Systemaufrufe.
  • Verwenden Sie Spies, wenn Sie Aufrufe überprüfen müssen, ohne die zugrunde liegende Implementierung zu ersetzen.
  • Verwenden Sie mongodb-memory-server für MongoDB-basierte Tests, damit Abfragen und Schema-Validierungen das tatsächliche Datenbankverhalten ausführen.

Wie finden Sie diese Anleitung?