Estándares y convenciones de código
Estándares de codificación, límites del espacio de trabajo y convenciones para contribuir a LibreChat.
Límites del área de trabajo
LibreChat es un monorepo. Todo código nuevo debe dirigirse al workspace correcto:
| Espacio de trabajo | Lenguaje | Lado | Propósito |
|---|---|---|---|
/api | JS (legado) | Backend | Servidor Express — minimizar cambios aquí |
/packages/api | TypeScript | Backend | El nuevo código de backend reside aquí (solo TS, consumido por /api) |
/packages/data-schemas | TypeScript | Backend | Modelos/esquemas de base de datos y lógica compartida específica de la base de datos |
/packages/data-provider | TypeScript | Compartido | Tipos de API, endpoint, servicio de datos — utilizado por el frontend y el backend |
/client | TypeScript/React | Frontend | SPA de frontend |
/packages/client | TypeScript | Frontend | Utilidades de frontend compartidas |
- Todo el código nuevo del backend debe estar en TypeScript en
/packages/api. - Mantenga los cambios en
/apial mínimo absoluto (envoltorios JS ligeros que llaman a/packages/api). - La lógica compartida específica de la base de datos se encuentra en
/packages/data-schemas. - La lógica de API compartida entre el frontend y el backend (endpoints, tipos, data-service) se encuentra en
/packages/data-provider. - Compila todo el código desde la raíz del proyecto:
npm run build. - Reconstruya el código del data-provider compartido después de cambios en la API o en los tipos:
npm run build:data-provider.
Pautas generales
- Utiliza principios de "clean code": mantén las funciones y los módulos pequeños, adhiérete al principio de responsabilidad única y escribe código expresivo y legible.
- Utiliza nombres de variables y funciones significativos y descriptivos.
- Priorice la legibilidad y la mantenibilidad del código por encima de la brevedad.
- Utiliza los archivos
.eslintrcy.prettierrcproporcionados para un formato de código consistente. - Corrija todos los errores de formato de lint utilizando la corrección automática cuando esté disponible. Todas las advertencias y errores de TypeScript/ESLint deben estar resueltos.
Naming and File Organization
- Utilice nombres de archivo de una sola palabra siempre que sea posible, como
permissions.ts,capabilities.tsoservice.ts. - Cuando se necesiten varias palabras, prefiera un directorio de una sola palabra que proporcione contexto al archivo, como
admin/capabilities.tsen lugar deadminCapabilities.ts. - Deja que el directorio proporcione contexto. Prefiere
app/service.tssobreapp/appConfigService.ts.
Estructura del código
- Nunca anidar: utilice retornos tempranos (early returns), código plano y una indentación mínima. Divida las operaciones complejas en funciones auxiliares con nombres descriptivos.
- Funcional primero: funciones puras, datos inmutables,
map/filter/reducesobre bucles imperativos. Solo recurra a la POO cuando mejore claramente el modelado de dominio o la encapsulación de estado. - Sin importaciones dinámicas a menos que sea absolutamente necesario.
- Extraiga la lógica repetida en funciones de utilidad dedicadas (DRY). Prefiera los ayudantes parametrizados, las constantes, los validadores compartidos, el manejo centralizado de errores y los tipos compartidos en lugar de implementaciones casi duplicadas.
Iteración y rendimiento
- Minimice los bucles — especialmente sobre estructuras de datos compartidas como las matrices de mensajes, que se iteran con frecuencia. Cada pasada adicional se acumula a gran escala.
- Consolide las operaciones secuenciales O(n) en una sola pasada siempre que sea posible; nunca recorra la misma colección dos veces si el trabajo puede combinarse.
- Elija estructuras de datos que reduzcan la necesidad de iterar (por ejemplo,
Map/Setpara búsquedas en lugar deArray.find/Array.includes). - Evite la creación innecesaria de objetos; considere las compensaciones entre espacio y tiempo.
- Evitar fugas de memoria: tenga cuidado con los closures, libere recursos/escuchadores de eventos y evite referencias circulares.
Seguridad de tipos
- Nunca uses
any. Tipos explícitos para todos los parámetros, valores de retorno y variables. - Limitar
unknown— eviteunknown,Record<string, unknown>y las asercionesas unknown as T. UnRecord<string, unknown>casi siempre indica una definición de tipo explícita faltante. - No dupliques tipos — verifica si un tipo ya existe en el proyecto (especialmente en
packages/data-provider) antes de definir uno nuevo. Reutiliza y extiende los tipos existentes. - Utilice tipos de unión, genéricos e interfaces de manera apropiada.
Comentarios y documentación
- Escriba código autodocumentado; no utilice comentarios en línea que narren lo que hace el código.
- JSDoc solo para lógica compleja/no evidente o para intellisense en APIs públicas.
- JSDoc de una sola línea para documentación breve, de varias líneas para casos complejos.
- Evite los comentarios
//independientes a menos que sea absolutamente necesario.
Orden de importación
Las importaciones están organizadas en tres secciones (en orden):
- Importaciones de paquetes — ordenadas de menor a mayor longitud de línea (
reactes siempre la primera importación). import typeimports — ordenadas de mayor a menor longitud (primero los tipos de paquetes, luego los tipos locales; el orden por longitud se reinicia entre subgrupos).- Importaciones locales/del proyecto — ordenadas de mayor a menor longitud.
- Consolide las importaciones de valores del mismo módulo tanto como sea posible.
- Utilice siempre
import type { ... }independiente para las importaciones de tipos; nunca utilice la palabra clavetypeen línea dentro de las importaciones de valores (por ejemplo,import { Foo, type Bar }es incorrecto).
Preferencias de bucle
- Limite los bucles tanto como sea posible. Prefiera las transformaciones de una sola pasada y evite reiterar sobre los mismos datos.
for (let i = 0; ...)para operaciones críticas de rendimiento o dependientes de índices.for...ofpara una iteración simple de arreglos.for...insolo para la enumeración de propiedades de objetos.
Servidor de API de Node.js
Diseño de la API
- Siga los principios RESTful al diseñar APIs.
- Utiliza nombres significativos y descriptivos para rutas, controladores, servicios y modelos.
- Utilice los métodos HTTP adecuados (GET, POST, PUT, DELETE) para cada ruta.
- Utilice códigos de estado y estructuras de respuesta adecuados para obtener respuestas de API consistentes (2xx para éxito, 4xx para solicitudes incorrectas del cliente, 5xx para errores del servidor).
- Utilice bloques try-catch para capturar y manejar excepciones de forma elegante.
- Implemente un manejo de errores adecuado y devuelva de manera consistente respuestas de error apropiadas.
- Utilice el sistema de registro incluido en el directorio
utilspara registrar eventos y errores importantes. - Realice una autenticación sin estado basada en JWT utilizando el middleware
requireJWTAuth.
Estructura de archivos
El nuevo código del backend se coloca en /packages/api como TypeScript. El directorio heredado /api sigue esta estructura:
Rutas
Especifica cada método de solicitud HTTP, cualquier middleware que se utilizará y la función del controlador que se llamará para cada ruta.
- Defina rutas utilizando el Express Router en archivos separados para cada recurso o agrupación lógica.
- Utilice nombres de ruta descriptivos y adhiérase a las convenciones RESTful.
- Mantén las rutas concisas y enfocadas en una sola responsabilidad.
- Anteponga el espacio de nombres
/apia todas las rutas.
Controladores
Contiene la lógica para cada ruta, incluyendo la llamada a las funciones de servicio correspondientes y la devolución del código de estado de respuesta y el cuerpo JSON adecuados.
- Cree un archivo de controlador independiente para cada ruta para manejar la lógica de solicitud/respuesta.
- Nombre los archivos de controlador usando la convención PascalCase y añada "Controller" al nombre del archivo (por ejemplo,
UserController.js). - Mantenga los controladores ligeros delegando las operaciones complejas a archivos de servicio o de modelo.
Servicios
Contiene lógica de negocio compleja u operaciones compartidas entre múltiples controladores.
- Nombre los archivos de servicio utilizando la convención PascalCase y añada "Service" al nombre del archivo (por ejemplo,
AuthService.js). - Evite el acoplamiento estrecho de servicios a modelos o bases de datos específicos para una mejor reutilización.
- Mantenga un principio de responsabilidad única dentro de cada servicio.
Modelos
Define modelos de Mongoose para representar entidades de datos y sus relaciones.
- Utilice nombres en singular y PascalCase para los archivos de modelo y sus colecciones asociadas (por ejemplo,
User.jsy la colecciónusers). - Incluya solo los campos, índices y validaciones necesarios en los modelos.
- Mantén los modelos independientes de la capa de API evitando referencias directas a objetos de solicitud/respuesta.
Acceso a la base de datos (MongoDB y Mongoose)
- Utilice Mongoose (https://mongoosejs.com) como el ODM de MongoDB.
- Cree archivos de modelo separados para cada entidad y asegure una clara separación de responsabilidades.
- Utilice la validación de esquemas de Mongoose para garantizar la integridad de los datos.
- Gestione las conexiones a la base de datos de manera eficiente y evite fugas de conexión.
- Utilice los generadores de consultas de Mongoose para crear consultas de base de datos concisas y legibles.
Cliente React
Mejores prácticas generales de TypeScript y React
- Utiliza las mejores prácticas de TypeScript para beneficiarte del tipado estático y de herramientas mejoradas.
- Agrupa los archivos relacionados dentro de directorios de funciones (por ejemplo,
SidePanel/Memories/). - Nombre los componentes utilizando la convención PascalCase.
- Utilice nombres concisos y descriptivos que reflejen con precisión el propósito del componente.
- Divide los componentes complejos en otros más pequeños y reutilizables cuando sea apropiado.
- Mantenga la lógica de renderizado dentro de los componentes al mínimo.
- Extraiga las partes reutilizables en funciones o hooks separados.
- Aplica definiciones de tipos de propiedades usando tipos o interfaces de TypeScript.
- Utilice la validación de formularios cuando sea apropiado (usamos React Hook Form para la validación y el envío de formularios).
Localización
- Todo el texto orientado al cliente debe localizarse utilizando el hook
useLocalize(). - Solo actualice las claves en inglés en
client/src/locales/en/translation.json(los otros idiomas se automatizan externamente). - Utilice prefijos de clave de localización semántica:
com_ui_,com_assistants_, etc. - Proporcione siempre un texto de respaldo significativo para las nuevas claves de localización.
Servicios de datos
- Cree hooks de proveedor de datos en
client/src/data-provider/[Feature]/queries.ts. - Exporte todos los hooks desde
client/src/data-provider/[Feature]/index.ts. - Añade las exportaciones de la funcionalidad al archivo principal
client/src/data-provider/index.ts. - Utilice React Query (
@tanstack/react-query) para todas las interacciones con la API. - Implementar la invalidación de consultas adecuada en las mutaciones.
- Agregue QueryKeys y MutationKeys a
packages/data-provider/src/keys.ts.
Al agregar una integración de API compartida, actualice:
packages/data-provider/src/api-endpoints.ts(endpoints)packages/data-provider/src/data-service.ts(funciones del servicio de datos)packages/data-provider/src/types/queries.ts(Tipos de TypeScript)
Rendimiento
- Priorice la eficiencia de memoria y velocidad a escala.
- Implementar una paginación por cursor adecuada para grandes conjuntos de datos.
- Evita renderizaciones innecesarias con arreglos de dependencias adecuados.
- Aprovecha las funciones de almacenamiento en caché y re-obtención en segundo plano de React Query.
Pruebas y documentación
- Escriba pruebas unitarias para todas las funcionalidades críticas y complejas utilizando Jest.
- Escriba pruebas de integración para todos los endpoint de la API utilizando Supertest.
- Escriba pruebas de extremo a extremo para todas las funcionalidades del lado del cliente utilizando Playwright.
- Utilice nombres de casos de prueba y funciones descriptivos para expresar claramente el propósito de la prueba.
- Ejecute las pruebas desde su directorio de espacio de trabajo:
cd api && npx jest <pattern>,cd packages/api && npx jest <pattern>, etc. - Cubrir los estados de carga, éxito y error para los flujos de interfaz de usuario/datos.
- Utilice
test/layout-test-utilspara renderizar componentes en las pruebas de frontend. - Prefiera la lógica real sobre los mocks. Utilice mocks solo para lo que no se pueda controlar localmente, como APIs HTTP externas, servicios con límites de tasa (rate-limited) y llamadas al sistema no deterministas.
- Utiliza spies cuando necesites verificar llamadas sin reemplazar la implementación subyacente.
- Utilice
mongodb-memory-serverpara pruebas respaldadas por MongoDB, de modo que las consultas y la validación de esquemas ejerciten el comportamiento real de la base de datos.
¿Qué te parece esta guía?