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

Standard e convenzioni del codice

Standard di codifica, confini dell'area di lavoro e convenzioni per contribuire a LibreChat.

Limiti dell'area di lavoro

LibreChat è un monorepo. Tutto il nuovo codice dovrebbe puntare al workspace corretto:

WorkspaceLinguaggioLatoScopo
/apiJS (legacy)BackendServer Express — ridurre al minimo le modifiche qui
/packages/apiTypeScriptBackendIl nuovo codice backend risiede qui (solo TS, consumato da /api)
/packages/data-schemasTypeScriptBackendModelli/schemi del database e logica condivisa specifica per il database
/packages/data-providerTypeScriptCondivisoTipi API, endpoint, data-service — utilizzati dal frontend e dal backend
/clientTypeScript/ReactFrontendSPA frontend
/packages/clientTypeScriptFrontendUtility frontend condivise
  • Tutto il nuovo codice backend deve essere in TypeScript in /packages/api.
  • Mantieni le modifiche a /api al minimo assoluto (wrapper JS sottili che richiamano /packages/api).
  • La logica condivisa specifica per il database si trova in /packages/data-schemas.
  • La logica API condivisa tra frontend e backend (endpoint, tipi, data-service) si trova in /packages/data-provider.
  • Compila tutto il codice dal percorso principale del progetto: npm run build.
  • Ricostruisci il codice del data-provider condiviso dopo modifiche all'API/ai tipi: npm run build:data-provider.

Linee guida generali

  • Usa i principi del "clean code": mantieni funzioni e moduli piccoli, attieniti al principio di singola responsabilità e scrivi codice espressivo e leggibile.
  • Usa nomi di variabili e funzioni significativi e descrittivi.
  • Dai priorità alla leggibilità e alla manutenibilità del codice rispetto alla brevità.
  • Utilizza i file .eslintrc e .prettierrc forniti per una formattazione del codice coerente.
  • Correggi tutti gli errori di formattazione del lint utilizzando la correzione automatica quando disponibile. Tutti gli avvisi e gli errori di TypeScript/ESLint devono essere risolti.

Denominazione e organizzazione dei file

  • Utilizza nomi di file composti da una sola parola quando possibile, come permissions.ts, capabilities.ts o service.ts.
  • Quando sono necessarie più parole, preferisci una directory a parola singola che fornisca il contesto del file, come admin/capabilities.ts invece di adminCapabilities.ts.
  • Lascia che sia la directory a fornire il contesto. Preferisci app/service.ts rispetto a app/appConfigService.ts.

Struttura del codice

  • Never-nesting: usa early return, codice piatto, indentazione minima. Suddividi le operazioni complesse in funzioni di supporto con nomi chiari.
  • Funzionale prima di tutto: funzioni pure, dati immutabili, map/filter/reduce al posto dei cicli imperativi. Ricorrere alla OOP solo quando migliora chiaramente la modellazione del dominio o l'incapsulamento dello stato.
  • Nessun import dinamico a meno che non sia assolutamente necessario.
  • Estrai la logica ripetuta in funzioni di utilità dedicate (DRY). Preferisci helper parametrizzati, costanti, validatori condivisi, gestione centralizzata degli errori e tipi condivisi rispetto a implementazioni quasi duplicate.

Iterazione e Prestazioni

  • Riduci al minimo i cicli — specialmente su strutture dati condivise come gli array di messaggi, che vengono iterati frequentemente. Ogni passaggio aggiuntivo si somma su larga scala.
  • Consolida le operazioni sequenziali O(n) in un unico passaggio quando possibile; non eseguire mai un ciclo sulla stessa collezione due volte se il lavoro può essere combinato.
  • Scegli strutture dati che riducano la necessità di iterare (ad esempio, Map/Set per le ricerche invece di Array.find/Array.includes).
  • Evita la creazione non necessaria di oggetti; valuta i compromessi tra spazio e tempo.
  • Prevenire i memory leak: prestare attenzione alle closure, rilasciare le risorse/event listener, evitare riferimenti circolari.

Type Safety

  • Non usare mai any. Tipi espliciti per tutti i parametri, i valori di ritorno e le variabili.
  • Limit unknown — evita unknown, Record<string, unknown> e le asserzioni as unknown as T. Un Record<string, unknown> segnala quasi sempre una definizione di tipo esplicita mancante.
  • Non duplicare i tipi — verifica se un tipo esiste già nel progetto (specialmente in packages/data-provider) prima di definirne uno nuovo. Riutilizza ed estendi i tipi esistenti.
  • Utilizza in modo appropriato i tipi unione (union types), i generici (generics) e le interfacce (interfaces).

Commenti e documentazione

  • Scrivi codice auto-esplicativo; nessun commento inline che narri cosa fa il codice.
  • JSDoc solo per logica complessa/non ovvia o per l'intellisense sulle API pubbliche.
  • JSDoc su riga singola per documentazione breve, su più righe per casi complessi.
  • Evita commenti // isolati a meno che non sia assolutamente necessario.

Ordine di importazione

Le importazioni sono organizzate in tre sezioni (in ordine):

  1. Import dei pacchetti — ordinati dalla lunghezza di riga più breve alla più lunga (react è sempre il primo import).
  2. import type imports — ordinati dal più lungo al più corto (prima i tipi dei pacchetti, poi i tipi locali; l'ordinamento per lunghezza si azzera tra i sottogruppi).
  3. Importazioni locali/di progetto — ordinate dalla più lunga alla più corta.
  • Consolida il più possibile le importazioni di valori dallo stesso modulo.
  • Utilizzare sempre import type { ... } autonomi per le importazioni di tipo; non utilizzare mai la parola chiave type inline all'interno delle importazioni di valori (ad esempio, import { Foo, type Bar } è errato).

Preferenze di Loop

  • Limita il più possibile i cicli. Prediligi trasformazioni a passaggio singolo ed evita di reiterare sugli stessi dati.
  • for (let i = 0; ...) per operazioni critiche per le prestazioni o dipendenti dall'indice.
  • for...of per una semplice iterazione di array.
  • for...in solo per l'enumerazione delle proprietà degli oggetti.

Server API Node.js

Progettazione API

  • Segui i principi RESTful durante la progettazione delle API.
  • Usa nomi significativi e descrittivi per route, controller, service e model.
  • Utilizza i metodi HTTP appropriati (GET, POST, PUT, DELETE) per ogni rotta.
  • Utilizza codici di stato e strutture di risposta appropriati per risposte API coerenti (2xx per successo, 4xx per richiesta errata dal client, 5xx per errore del server).
  • Usa blocchi try-catch per intercettare e gestire le eccezioni in modo appropriato.
  • Implementa una corretta gestione degli errori e restituisci in modo coerente risposte di errore appropriate.
  • Utilizza il sistema di logging incluso nella directory utils per registrare eventi ed errori importanti.
  • Esegui l'autenticazione stateless basata su JWT utilizzando il middleware requireJWTAuth.

Struttura dei file

Il nuovo codice backend va in /packages/api come TypeScript. La directory legacy /api segue questa struttura:

Percorsi

Specifica ogni metodo di richiesta HTTP, qualsiasi middleware da utilizzare e la funzione del controller da richiamare per ogni rotta.

  • Definisci le route utilizzando l'Express Router in file separati per ogni risorsa o raggruppamento logico.
  • Utilizza nomi di percorso descrittivi e attieniti alle convenzioni RESTful.
  • Mantieni le route concise e focalizzate su una singola responsabilità.
  • Anteponi il namespace /api a tutti i percorsi.

Controller

Contiene la logica per ogni route, inclusa la chiamata alle funzioni di servizio appropriate e la restituzione del codice di stato della risposta e del corpo JSON appropriati.

  • Crea un file controller separato per ogni rotta per gestire la logica di richiesta/risposta.
  • Assegna ai file controller nomi che utilizzano la convenzione PascalCase e aggiungi "Controller" al nome del file (ad esempio, UserController.js).
  • Mantieni i controller leggeri delegando le operazioni complesse ai file di servizio o di modello.

Servizi

Contiene logica di business complessa o operazioni condivise tra più controller.

  • Assegna un nome ai file di servizio utilizzando la convenzione PascalCase e aggiungi "Service" al nome del file (ad esempio, AuthService.js).
  • Evita di accoppiare strettamente i servizi a modelli o database specifici per una migliore riutilizzabilità.
  • Mantenere il principio di singola responsabilità all'interno di ogni servizio.

Modelli

Definisce i modelli Mongoose per rappresentare le entità dei dati e le loro relazioni.

  • Utilizza nomi singolari in PascalCase per i file dei modelli e le relative collezioni (ad esempio, User.js e la collezione users).
  • Includi solo i campi, gli indici e le validazioni necessari nei modelli.
  • Mantieni i modelli indipendenti dal livello API evitando riferimenti diretti agli oggetti di richiesta/risposta.

Accesso al database (MongoDB e Mongoose)

  • Utilizza Mongoose (https://mongoosejs.com) come ODM per MongoDB.
  • Crea file di modello separati per ogni entità e assicurati una chiara separazione delle responsabilità.
  • Utilizza la validazione dello schema Mongoose per garantire l'integrità dei dati.
  • Gestisci le connessioni al database in modo efficiente ed evita perdite di connessione.
  • Utilizza i query builder di Mongoose per creare query al database concise e leggibili.

Client React

Best practice generali per TypeScript e React

  • Utilizza le best practice di TypeScript per beneficiare della tipizzazione statica e di strumenti migliorati.
  • Raggruppa i file correlati all'interno delle directory delle funzionalità (ad esempio, SidePanel/Memories/).
  • Assegna un nome ai componenti utilizzando la convenzione PascalCase.
  • Utilizza nomi concisi e descrittivi che riflettano accuratamente lo scopo del componente.
  • Suddividi i componenti complessi in componenti più piccoli e riutilizzabili quando appropriato.
  • Mantieni la logica di rendering all'interno dei componenti al minimo.
  • Estrai le parti riutilizzabili in funzioni o hook separati.
  • Applica le definizioni dei tipi delle prop utilizzando tipi o interfacce TypeScript.
  • Utilizza la convalida dei moduli dove appropriato (utilizziamo React Hook Form per la convalida e l'invio dei moduli).

Localizzazione

  • Tutto il testo rivolto all'utente deve essere localizzato utilizzando l'hook useLocalize().
  • Aggiorna solo le chiavi in inglese in client/src/locales/en/translation.json (le altre lingue vengono gestite automaticamente dall'esterno).
  • Utilizza prefissi chiave di localizzazione semantica: com_ui_, com_assistants_, ecc.
  • Fornisci sempre un testo di fallback significativo per le nuove chiavi di localizzazione.

Servizi dati

  • Crea gli hook del data provider in client/src/data-provider/[Feature]/queries.ts.
  • Esporta tutti gli hook da client/src/data-provider/[Feature]/index.ts.
  • Aggiungi le esportazioni delle funzionalità al file principale client/src/data-provider/index.ts.
  • Utilizza React Query (@tanstack/react-query) per tutte le interazioni API.
  • Implementare una corretta invalidazione delle query sulle mutazioni.
  • Aggiungi QueryKeys e MutationKeys a packages/data-provider/src/keys.ts.

Quando aggiungi un'integrazione API condivisa, aggiorna:

  • packages/data-provider/src/api-endpoints.ts (endpoint)
  • packages/data-provider/src/data-service.ts (funzioni del data service)
  • packages/data-provider/src/types/queries.ts (Tipi TypeScript)

Prestazioni

  • Dai priorità all'efficienza della memoria e della velocità su larga scala.
  • Implementa una paginazione a cursore adeguata per set di dati di grandi dimensioni.
  • Evita re-render non necessari con array di dipendenze corretti.
  • Sfrutta le funzionalità di caching e di refetching in background di React Query.

Test e documentazione

  • Scrivi unit test per tutte le funzionalità critiche e complesse utilizzando Jest.
  • Scrivi test di integrazione per tutti gli endpoint API utilizzando Supertest.
  • Scrivi test end-to-end per tutte le funzionalità lato client utilizzando Playwright.
  • Utilizza nomi di casi di test e funzioni descrittivi per esprimere chiaramente lo scopo del test.
  • Esegui i test dalla loro directory di workspace: cd api && npx jest <pattern>, cd packages/api && npx jest <pattern>, ecc.
  • Coprire gli stati di caricamento, successo ed errore per i flussi di interfaccia utente/dati.
  • Utilizza test/layout-test-utils per il rendering dei componenti nei test del frontend.
  • Preferisci la logica reale ai mock. Esegui il mock solo di ciò che non può essere controllato localmente, come API HTTP esterne, servizi con limitazioni di frequenza (rate-limited) e chiamate di sistema non deterministiche.
  • Usa gli spy quando hai bisogno di verificare le chiamate senza sostituire l'implementazione sottostante.
  • Utilizza mongodb-memory-server per i test basati su MongoDB, in modo che le query e la validazione dello schema verifichino il comportamento reale del database.

Com’è questa guida?