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:
| Workspace | Linguagem | Lado | Propósito |
|---|---|---|---|
/api | JS (legado) | Backend | Servidor Express — minimize alterações aqui |
/packages/api | TypeScript | Backend | Novo código de backend reside aqui (apenas TS, consumido pelo /api) |
/packages/data-schemas | TypeScript | Backend | Modelos/schemas de banco de dados e lógica compartilhada específica do banco de dados |
/packages/data-provider | TypeScript | Compartilhado | Tipos de API, endpoints, data-service — usados pelo frontend e backend |
/client | TypeScript/React | Frontend | SPA de frontend |
/packages/client | TypeScript | Frontend | Utilitários de frontend compartilhados |
- Todo novo código de backend deve ser em TypeScript em
/packages/api. - Mantenha as alterações em
/apino 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
.eslintrce.prettierrcfornecidos 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.tsouservice.ts. - Quando várias palavras forem necessárias, prefira um diretório de palavra única que forneça contexto ao arquivo, como
admin/capabilities.tsem vez deadminCapabilities.ts. - Deixe que o diretório forneça o contexto. Prefira
app/service.tsem vez deapp/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/reduceem 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/Setpara buscas em vez deArray.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— eviteunknown,Record<string, unknown>e asserçõesas unknown as T. UmRecord<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):
- Importações de pacotes — ordenadas do menor para o maior comprimento de linha (
reacté sempre a primeira importação). import typeimports — ordenados do maior para o menor (tipos de pacotes primeiro, depois tipos locais; a ordenação por tamanho é reiniciada entre os subgrupos).- 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-chavetypeembutida 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...ofpara iteração simples de arrays.for...inapenas 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
utilspara 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.jse a coleçãousers). - 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.tsprincipal. - 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-utilspara 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-serverpara 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?