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:
| Workspace | Linguaggio | Lato | Scopo |
|---|---|---|---|
/api | JS (legacy) | Backend | Server Express — ridurre al minimo le modifiche qui |
/packages/api | TypeScript | Backend | Il nuovo codice backend risiede qui (solo TS, consumato da /api) |
/packages/data-schemas | TypeScript | Backend | Modelli/schemi del database e logica condivisa specifica per il database |
/packages/data-provider | TypeScript | Condiviso | Tipi API, endpoint, data-service — utilizzati dal frontend e dal backend |
/client | TypeScript/React | Frontend | SPA frontend |
/packages/client | TypeScript | Frontend | Utility frontend condivise |
- Tutto il nuovo codice backend deve essere in TypeScript in
/packages/api. - Mantieni le modifiche a
/apial 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
.eslintrce.prettierrcforniti 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.tsoservice.ts. - Quando sono necessarie più parole, preferisci una directory a parola singola che fornisca il contesto del file, come
admin/capabilities.tsinvece diadminCapabilities.ts. - Lascia che sia la directory a fornire il contesto. Preferisci
app/service.tsrispetto aapp/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/reduceal 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/Setper le ricerche invece diArray.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— evitaunknown,Record<string, unknown>e le asserzionias unknown as T. UnRecord<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):
- Import dei pacchetti — ordinati dalla lunghezza di riga più breve alla più lunga (
reactè sempre il primo import). import typeimports — 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).- 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 chiavetypeinline 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...ofper una semplice iterazione di array.for...insolo 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
utilsper 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
/apia 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.jse la collezioneusers). - 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-utilsper 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-serverper i test basati su MongoDB, in modo che le query e la validazione dello schema verifichino il comportamento reale del database.
Com’è questa guida?