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

Padrões e Convenções de Código

Padrões de codificação, limites de workspace e convenções para contribuir com o LibreChat.

Limites do Workspace

LibreChat é um monorepo. Todo código novo deve ter como alvo o workspace correto:

WorkspaceLinguagemLadoPropósito
/apiJS (legado)BackendServidor Express — minimize alterações aqui
/packages/apiTypeScriptBackendNovo código de backend reside aqui (apenas TS, consumido pelo /api)
/packages/data-schemasTypeScriptBackendModelos/schemas de banco de dados e lógica compartilhada específica do banco de dados
/packages/data-providerTypeScriptCompartilhadoTipos de API, endpoints, data-service — usados pelo frontend e backend
/clientTypeScript/ReactFrontendSPA de frontend
/packages/clientTypeScriptFrontendUtilitários de frontend compartilhados
  • Todo novo código de backend deve ser em TypeScript em /packages/api.
  • Mantenha as alterações em /api no mínimo absoluto (wrappers JS leves chamando /packages/api).
  • A lógica compartilhada específica do banco de dados fica em /packages/data-schemas.
  • A lógica de API compartilhada entre frontend/backend (endpoints, types, data-service) fica em /packages/data-provider.
  • Compile todo o código do projeto a partir da raiz: npm run build.
  • Recompile o código do data-provider compartilhado após alterações na API/tipos: npm run build:data-provider.

Diretrizes Gerais

  • Use os princípios de "clean code": mantenha funções e módulos pequenos, siga o princípio da responsabilidade única e escreva código expressivo e legível.
  • Use nomes de variáveis e funções significativos e descritivos.
  • Priorize a legibilidade e a manutenibilidade do código em vez da brevidade.
  • Use os arquivos .eslintrc e .prettierrc fornecidos para uma formatação de código consistente.
  • Corrija todos os erros de formatação de lint usando a correção automática quando disponível. Todos os avisos e erros de TypeScript/ESLint devem ser resolvidos.

Naming and File Organization

  • Use nomes de arquivo de palavra única sempre que possível, como permissions.ts, capabilities.ts ou service.ts.
  • Quando várias palavras forem necessárias, prefira um diretório de palavra única que forneça contexto ao arquivo, como admin/capabilities.ts em vez de adminCapabilities.ts.
  • Deixe que o diretório forneça o contexto. Prefira app/service.ts em vez de app/appConfigService.ts.

Estrutura do Código

  • Never-nesting: use retornos antecipados (early returns), código plano, indentação mínima. Divida operações complexas em funções auxiliares bem nomeadas.
  • Funcional em primeiro lugar: funções puras, dados imutáveis, map/filter/reduce em vez de loops imperativos. Recorra à POO apenas quando isso melhorar claramente a modelagem de domínio ou o encapsulamento de estado.
  • Sem imports dinâmicos, a menos que seja absolutamente necessário.
  • Extraia a lógica repetida para funções utilitárias dedicadas (DRY). Prefira auxiliares parametrizados, constantes, validadores compartilhados, tratamento de erros centralizado e tipos compartilhados em vez de implementações quase duplicadas.

Iteração e Desempenho

  • Minimize looping — especialmente sobre estruturas de dados compartilhadas, como arrays de mensagens, que são iteradas frequentemente. Cada passagem adicional acumula impacto em escala.
  • Consolide operações O(n) sequenciais em uma única passagem sempre que possível; nunca percorra a mesma coleção duas vezes se o trabalho puder ser combinado.
  • Escolha estruturas de dados que reduzam a necessidade de iteração (por exemplo, Map/Set para buscas em vez de Array.find/Array.includes).
  • Evite a criação desnecessária de objetos; considere as compensações entre espaço e tempo.
  • Evite vazamentos de memória: tenha cuidado com closures, descarte recursos/ouvintes de eventos e evite referências circulares.

Segurança de Tipagem

  • Nunca use any. Tipos explícitos para todos os parâmetros, valores de retorno e variáveis.
  • Limitar unknown — evite unknown, Record<string, unknown> e asserções as unknown as T. Um Record<string, unknown> quase sempre sinaliza uma definição de tipo explícita ausente.
  • Não duplique tipos — verifique se um tipo já existe no projeto (especialmente em packages/data-provider) antes de definir um novo. Reutilize e estenda os tipos existentes.
  • Use union types, generics e interfaces de forma apropriada.

Comentários e Documentação

  • Escreva código autodocumentado; sem comentários em linha narrando o que o código faz.
  • JSDoc apenas para lógica complexa/não óbvia ou intellisense em APIs públicas.
  • JSDoc de linha única para documentação breve, de múltiplas linhas para casos complexos.
  • Evite comentários // isolados, a menos que seja absolutamente necessário.

Ordem de Importação

As importações são organizadas em três seções (em ordem):

  1. Importações de pacotes — ordenadas do menor para o maior comprimento de linha (react é sempre a primeira importação).
  2. import type imports — ordenados do maior para o menor (tipos de pacotes primeiro, depois tipos locais; a ordenação por tamanho é reiniciada entre os subgrupos).
  3. Importações locais/de projeto — ordenadas da mais longa para a mais curta.
  • Consolide as importações de valores do mesmo módulo tanto quanto possível.
  • Sempre use import type { ... } isolado para importações de tipos; nunca use a palavra-chave type embutida dentro de importações de valores (por exemplo, import { Foo, type Bar } está incorreto).

Preferências de Loop

  • Limite o looping tanto quanto possível. Prefira transformações de passagem única e evite iterar sobre os mesmos dados repetidamente.
  • for (let i = 0; ...) para operações críticas de desempenho ou dependentes de índice.
  • for...of para iteração simples de arrays.
  • for...in apenas para enumeração de propriedades de objetos.

Servidor de API Node.js

Design da API

  • Siga os princípios RESTful ao projetar APIs.
  • Use nomes significativos e descritivos para rotas, controladores, serviços e modelos.
  • Use métodos HTTP apropriados (GET, POST, PUT, DELETE) para cada rota.
  • Use códigos de status e estruturas de resposta adequados para respostas de API consistentes (2xx para sucesso, 4xx para solicitações incorretas do cliente, 5xx para erros de servidor).
  • Use blocos try-catch para capturar e tratar exceções de forma elegante.
  • Implemente um tratamento de erros adequado e retorne consistentemente respostas de erro apropriadas.
  • Use o sistema de log incluído no diretório utils para registrar eventos importantes e erros.
  • Faça autenticação stateless baseada em JWT usando o middleware requireJWTAuth.

Estrutura de Arquivos

O novo código de backend vai em /packages/api como TypeScript. O diretório legado /api segue esta estrutura:

Rotas

Especifica cada método de requisição HTTP, qualquer middleware a ser utilizado e a função de controlador a ser chamada para cada rota.

  • Defina rotas usando o Express Router em arquivos separados para cada recurso ou agrupamento lógico.
  • Use nomes de rota descritivos e siga as convenções RESTful.
  • Mantenha as rotas concisas e focadas em uma única responsabilidade.
  • Prefixe todas as rotas com o namespace /api.

Controllers

Contém a lógica para cada rota, incluindo a chamada das funções de serviço apropriadas e o retorno do código de status de resposta e corpo JSON adequados.

  • Crie um arquivo de controlador separado para cada rota para lidar com a lógica de solicitação/resposta.
  • Nomeie os arquivos de controller usando a convenção PascalCase e adicione "Controller" ao final do nome do arquivo (por exemplo, UserController.js).
  • Mantenha os controllers enxutos delegando operações complexas para arquivos de service ou model.

Serviços

Contém lógica de negócios complexa ou operações compartilhadas entre múltiplos controllers.

  • Nomeie os arquivos de serviço usando a convenção PascalCase e adicione "Service" ao final do nome do arquivo (por exemplo, AuthService.js).
  • Evite o acoplamento rígido de serviços a modelos ou bancos de dados específicos para obter melhor reutilização.
  • Mantenha o princípio de responsabilidade única dentro de cada serviço.

Modelos

Define modelos Mongoose para representar entidades de dados e seus relacionamentos.

  • Use nomes no singular e em PascalCase para arquivos de modelo e suas coleções associadas (por exemplo, User.js e a coleção users).
  • Inclua apenas os campos, índices e validações necessários nos models.
  • Mantenha os modelos independentes da camada de API, evitando referências diretas a objetos de solicitação/resposta.

Acesso ao Banco de Dados (MongoDB e Mongoose)

  • Use o Mongoose (https://mongoosejs.com) como o ODM do MongoDB.
  • Crie arquivos de modelo separados para cada entidade e garanta uma clara separação de responsabilidades.
  • Use a validação de schema do Mongoose para garantir a integridade dos dados.
  • Gerencie conexões de banco de dados de forma eficiente e evite vazamentos de conexão.
  • Use construtores de consulta do Mongoose para criar consultas de banco de dados concisas e legíveis.

Cliente React

Melhores Práticas Gerais de TypeScript e React

  • Use TypeScript best practices para se beneficiar da tipagem estática e de ferramentas aprimoradas.
  • Agrupe arquivos relacionados dentro de diretórios de recursos (por exemplo, SidePanel/Memories/).
  • Nomeie componentes usando a convenção PascalCase.
  • Use nomes concisos e descritivos que reflitam com precisão o propósito do componente.
  • Divida componentes complexos em outros menores e reutilizáveis quando apropriado.
  • Mantenha a lógica de renderização dentro dos componentes mínima.
  • Extraia partes reutilizáveis para funções ou hooks separados.
  • Aplique definições de tipo de propriedade usando types ou interfaces do TypeScript.
  • Use a validação de formulário onde apropriado (nós usamos o React Hook Form para validação e envio de formulários).

Localização

  • Todo texto voltado ao cliente deve ser localizado usando o hook useLocalize().
  • Atualize apenas as chaves em inglês no arquivo client/src/locales/en/translation.json (outros idiomas são automatizados externamente).
  • Use prefixos de chave de localização semântica: com_ui_, com_assistants_, etc.
  • Sempre forneça um texto de fallback significativo para novas chaves de localização.

Serviços de Dados

  • Crie hooks de provedor de dados em client/src/data-provider/[Feature]/queries.ts.
  • Exporte todos os hooks de client/src/data-provider/[Feature]/index.ts.
  • Adicione as exportações de funcionalidade ao client/src/data-provider/index.ts principal.
  • Use React Query (@tanstack/react-query) para todas as interações com a API.
  • Implemente a invalidação de consulta adequada em mutations.
  • Adicione QueryKeys e MutationKeys em packages/data-provider/src/keys.ts.

Ao adicionar uma integração de API compartilhada, atualize:

  • packages/data-provider/src/api-endpoints.ts (endpoints)
  • packages/data-provider/src/data-service.ts (funções de serviço de dados)
  • packages/data-provider/src/types/queries.ts (Tipos TypeScript)

Desempenho

  • Priorize a eficiência de memória e velocidade em escala.
  • Implemente a paginação por cursor adequada para grandes conjuntos de dados.
  • Evite renderizações desnecessárias com arrays de dependência adequados.
  • Aproveite os recursos de cache e refetch em segundo plano do React Query.

Testes e Documentação

  • Escreva testes unitários para todas as funcionalidades críticas e complexas usando Jest.
  • Escreva testes de integração para todos os endpoint da API usando Supertest.
  • Escreva testes de ponta a ponta para todas as funcionalidades do lado do cliente usando Playwright.
  • Use nomes descritivos para casos de teste e funções para expressar claramente o propósito do teste.
  • Execute os testes a partir do diretório do seu workspace: cd api && npx jest <pattern>, cd packages/api && npx jest <pattern>, etc.
  • Cubra os estados de carregamento, sucesso e erro para fluxos de UI/dados.
  • Use test/layout-test-utils para renderizar componentes em testes de frontend.
  • Prefira lógica real em vez de mocks. Use mocks apenas para o que não pode ser controlado localmente, como APIs HTTP externas, serviços com limite de taxa (rate-limited) e chamadas de sistema não determinísticas.
  • Use spies quando precisar validar chamadas sem substituir a implementação subjacente.
  • Use mongodb-memory-server para testes baseados em MongoDB, para que as consultas e a validação de esquema exercitem o comportamento real do banco de dados.

Como está este guia?