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

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 trabajoLenguajeLadoPropósito
/apiJS (legado)BackendServidor Express — minimizar cambios aquí
/packages/apiTypeScriptBackendEl nuevo código de backend reside aquí (solo TS, consumido por /api)
/packages/data-schemasTypeScriptBackendModelos/esquemas de base de datos y lógica compartida específica de la base de datos
/packages/data-providerTypeScriptCompartidoTipos de API, endpoint, servicio de datos — utilizado por el frontend y el backend
/clientTypeScript/ReactFrontendSPA de frontend
/packages/clientTypeScriptFrontendUtilidades de frontend compartidas
  • Todo el código nuevo del backend debe estar en TypeScript en /packages/api.
  • Mantenga los cambios en /api al 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 .eslintrc y .prettierrc proporcionados 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.ts o service.ts.
  • Cuando se necesiten varias palabras, prefiera un directorio de una sola palabra que proporcione contexto al archivo, como admin/capabilities.ts en lugar de adminCapabilities.ts.
  • Deja que el directorio proporcione contexto. Prefiere app/service.ts sobre app/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/reduce sobre 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/Set para búsquedas en lugar de Array.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 — evite unknown, Record<string, unknown> y las aserciones as unknown as T. Un Record<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):

  1. Importaciones de paquetes — ordenadas de menor a mayor longitud de línea (react es siempre la primera importación).
  2. import type imports — ordenadas de mayor a menor longitud (primero los tipos de paquetes, luego los tipos locales; el orden por longitud se reinicia entre subgrupos).
  3. 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 clave type en 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...of para una iteración simple de arreglos.
  • for...in solo 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 utils para 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 /api a 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.js y la colección users).
  • 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-utils para 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-server para 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?