Home > Blog > Desenvolvimento Web
Desenvolvimento Web
JavaScript
Programação

Meilisearch no Node.js

Atualizado em: 17 de setembro de 2026

Estação de trabalho usada no desenvolvimento de aplicações Node.js

O Meilisearch no Node.js adiciona busca rápida com tolerância a erros de digitação, filtros, facetas e ranking configurável. Ele é útil para catálogos, documentação, marketplaces, painéis administrativos e qualquer interface em que o usuário espera resultados enquanto digita.

Meilisearch não deve ser tratado como fonte principal de dados. PostgreSQL ou outro banco continua responsável por transações e consistência. O índice é uma projeção otimizada para leitura, atualizada por jobs, eventos ou Outbox Pattern.

Neste guia, você aprenderá a usar o SDK JavaScript, criar índices, enviar documentos em lotes, aguardar tasks, configurar searchable e filterable attributes, aplicar filtros por tenant, paginar, destacar resultados e manter o índice sincronizado com segurança.

O que é Meilisearch?

A documentação oficial de primeiros passos do Meilisearch apresenta índices como estruturas que recebem documentos e atendem consultas por API. O projeto pode ser usado na nuvem ou self-hosted.

O SDK JavaScript está documentado em Meilisearch API.

Instalação

npm install meilisearch

Criando o cliente

import { MeiliSearch } from 'meilisearch';

export const search = new MeiliSearch({
  host: process.env.MEILISEARCH_HOST ?? 'http://localhost:7700',
  apiKey: process.env.MEILISEARCH_API_KEY
});

Use uma key administrativa somente no backend. O navegador deve receber uma search key restrita, nunca a master key.

Criando um índice

const task = await search.createIndex('products', {
  primaryKey: 'id'
});

await search.waitForTask(task.taskUid);

Operações de configuração e indexação são assíncronas. A API retorna um task UID. Aguarde ou monitore o status antes de assumir que a mudança está disponível.

Adicionando documentos

const index = search.index('products');

const task = await index.addDocuments([
  {
    id: 'product-42',
    tenantId: 'tenant-a',
    name: 'Teclado Mecânico',
    description: 'Teclado compacto com switches táteis',
    category: 'peripherals',
    priceCents: 45990,
    inStock: true,
    updatedAt: '2026-09-17T18:00:00Z'
  }
]);

await search.waitForTask(task.taskUid);

Meilisearch substitui documentos com a mesma primary key quando usa addDocuments. Envie campos completos ou entenda a diferença para updates parciais.

Indexação em lotes

Não envie um request por documento:

for (const batch of chunk(products, 1000)) {
  const task = await index.addDocuments(batch);
  await search.waitForTask(task.taskUid);
}

O tamanho ideal depende do documento, rede e capacidade do servidor. Monitore duração e memória.

Primeira busca

const result = await index.search('tecldo mecanico', {
  limit: 20
});

console.log(result.hits);

A tolerância a typos ajuda a encontrar “teclado mecânico” mesmo com erros, mas precisa ser avaliada para IDs, códigos e termos curtos.

Searchable attributes

const task = await index.updateSearchableAttributes([
  'name',
  'description',
  'category'
]);

await search.waitForTask(task.taskUid);

A ordem influencia prioridade. Coloque campos mais importantes primeiro. Não indexe texto sem valor de busca.

Filterable attributes

const task = await index.updateFilterableAttributes([
  'tenantId',
  'category',
  'inStock',
  'priceCents'
]);

Um campo só pode ser usado em filtros depois de configurado. Alterações de settings podem reindexar documentos.

Filtro por tenant

const result = await index.search(query, {
  filter: [
    `tenantId = "${safeTenantId}"`,
    'inStock = true'
  ]
});

Não monte filtros com texto arbitrário do usuário. Use valores validados e escape conforme o SDK. Mais importante: uma search key pública precisa restringir o tenant, pois esconder o filtro na interface não é autorização.

Tenant tokens

Meilisearch oferece mecanismos de tokens ou search rules para limitar filtros. Gere-os no backend e use expiração curta. Nunca deixe o cliente escolher um filtro de tenant sem restrição criptográfica.

Veja Multi-Tenancy no Node.js.

Sortable attributes

await index.updateSortableAttributes([
  'priceCents',
  'updatedAt'
]);

Uso:

await index.search(query, {
  sort: ['priceCents:asc']
});

Ordenar apenas por preço pode reduzir relevância textual. Ofereça modos de ordenação explícitos.

Facetas

const result = await index.search(query, {
  facets: ['category', 'inStock'],
  limit: 20
});

Facetas alimentam filtros e contagens. Campos de alta cardinalidade podem aumentar custo.

Ranking rules

Meilisearch usa regras como words, typo, proximity, attribute, sort e exactness. Personalizações precisam ser avaliadas com um conjunto de consultas reais.

await index.updateRankingRules([
  'words',
  'typo',
  'proximity',
  'attribute',
  'sort',
  'exactness'
]);

Sinônimos

await index.updateSynonyms({
  laptop: ['notebook'],
  celular: ['smartphone']
});

Sinônimos ajudam vocabulário de negócio, mas podem criar resultados excessivos. Versione e teste alterações.

Stop words

await index.updateStopWords([
  'de', 'da', 'do', 'e', 'para'
]);

Não remova palavras que fazem diferença no domínio. Em títulos curtos, stop words podem mudar a intenção.

Destacando resultados

const result = await index.search(query, {
  attributesToHighlight: ['name', 'description'],
  highlightPreTag: '<mark>',
  highlightPostTag: '</mark>'
});

Sanitize o HTML antes de renderizar. O conteúdo indexado pode ter vindo de usuário.

Recorte de texto

await index.search(query, {
  attributesToCrop: ['description'],
  cropLength: 20,
  cropMarker: '…'
});

Isso reduz payload e melhora a apresentação.

Paginação

Para interfaces simples:

await index.search(query, {
  offset: 40,
  limit: 20
});

Para obter total e páginas, use o modo de paginação suportado pela versão do SDK. Grandes offsets aumentam custo; limite profundidade ou use navegação adequada ao produto.

Campos retornados

await index.search(query, {
  attributesToRetrieve: [
    'id', 'name', 'priceCents', 'inStock'
  ]
});

Não retorne campos internos ou sensíveis apenas porque estão no documento.

Fonte de verdade

Após o usuário selecionar um resultado, busque o registro atualizado no banco. Estoque, preço e permissão podem ter mudado depois da indexação.

Sincronização com Outbox

  1. transação atualiza o produto;
  2. grava um evento na outbox;
  3. worker lê o evento;
  4. atualiza Meilisearch;
  5. marca o evento como concluído;
  6. retries reutilizam a mesma chave.

Consulte Outbox Pattern no Node.js.

Atualização idempotente

Use o ID do banco como primary key. Repetir a atualização produz o mesmo documento. Veja Idempotência em APIs Node.js.

Exclusão

const task = await index.deleteDocument('product-42');
await search.waitForTask(task.taskUid);

Exclusões também são assíncronas. Para requisitos de privacidade, monitore conclusão e cópias de backup.

Reindexação sem downtime

Quando o schema muda:

  1. crie products_v2;
  2. aplique settings;
  3. carregue documentos;
  4. execute testes;
  5. troque o índice usado pela aplicação;
  6. remova a versão antiga depois.

Use aliases ou estratégia equivalente suportada pela plataforma.

Busca híbrida e vetorial

Versões atuais podem oferecer recursos de busca semântica. Avalie custo, modelo, armazenamento de embeddings e filtros. Para PostgreSQL com vetores, consulte pgvector no Node.js.

Timeout

const result = await Promise.race([
  index.search(query, options),
  new Promise((_, reject) =>
    setTimeout(() => reject(new Error('Search timeout')), 1500)
  )
]);

Prefira AbortSignal quando o SDK suportar. Defina fallback para indisponibilidade.

Fallback

Uma API pode:

  • retornar cache recente;
  • usar busca limitada no PostgreSQL;
  • mostrar mensagem de indisponibilidade;
  • desativar sugestões enquanto mantém outras rotas.

Use Circuit Breaker quando falhas repetidas sobrecarregam o serviço. Veja Circuit Breaker no Node.js.

API keys

  • Master key apenas em ambiente administrativo.
  • Key de ingestão no worker.
  • Search key no backend ou frontend.
  • Tenant token para isolamento.
  • Rotação e escopo mínimo.

Veja Gestão de Segredos no Node.js.

Observabilidade

Monitore:

  • latência de busca p50, p95 e p99;
  • zero-result rate;
  • consultas mais frequentes;
  • tasks falhas;
  • lag da indexação;
  • tamanho dos índices;
  • tempo de reindexação;
  • uso de memória e disco.

Qualidade

Crie um conjunto de consultas e resultados esperados. Meça:

  • precision@k;
  • MRR;
  • taxa de clique;
  • conversão;
  • zero results;
  • reformulações da consulta.

Ranking deve ser avaliado com comportamento do usuário, não apenas por exemplos manuais.

Testes

Use Meilisearch real em container. Teste settings, filtros de tenant, typos, facetas, tasks, reindexação, exclusão e falhas.

Consulte Testcontainers no Node.js.

Erros comuns

  • Master key no navegador: comprometimento total.
  • Sem filtro de tenant: vazamento de dados.
  • Indexar um por vez: throughput baixo.
  • Ignorar tasks: aplicação assume mudança inexistente.
  • Índice como fonte de verdade: preço e estoque ficam incorretos.
  • Ranking sem avaliação: resultados ruins.
  • HTML destacado sem sanitização: XSS.
  • Reindexar no índice ativo: busca fica inconsistente.

Conclusão

O Meilisearch no Node.js entrega busca instantânea com tolerância a typos, filtros, facetas e ranking configurável. O SDK JavaScript simplifica índices, documentos, settings e consultas.

Mantenha o banco como fonte de verdade, sincronize por eventos, use lotes e aguarde tasks. Proteja tenant e API keys, teste relevância e faça reindexações versionadas. Assim, a busca melhora a experiência sem assumir responsabilidades transacionais que pertencem ao banco principal.

10 melhores cursos de programação em 2026

Descubra os melhores cursos de programação. Aprenda a escolher o curso ideal para iniciar ou avançar na carreira de desenvolvedor

POSTS RELACIONADOS

Ver todos

Seta para a direita