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

esbuild no Node.js

Atualizado em: 10 de setembro de 2026

Terminal Linux usado em desenvolvimento e administração de aplicações Node.js

O esbuild no Node.js é uma ferramenta de build extremamente rápida para transformar TypeScript e JavaScript, gerar bundles, minificar código e produzir sourcemaps. Ela pode ser usada pela linha de comando ou pela API JavaScript, o que facilita integrar o build a scripts próprios, monorepos e pipelines de CI.

Apesar da velocidade, esbuild não substitui todas as etapas de um projeto. Ele remove tipos, mas não realiza verificação completa de TypeScript; também não executa testes, não publica pacotes e não corrige automaticamente incompatibilidades de runtime. O melhor uso combina esbuild com tsc --noEmit, testes e validação do artefato final.

Neste guia, você aprenderá a instalar esbuild, criar bundles para Node.js, configurar ESM e CommonJS, marcar dependências externas, gerar sourcemaps, usar plugins, watch mode, metafile e boas práticas para aplicações e bibliotecas.

Instalação

npm install --save-dev esbuild

Use uma versão local fixada pelo lockfile. Evite depender de instalação global em equipes e pipelines.

Primeiro build

npx esbuild src/server.ts \
  --bundle \
  --platform=node \
  --target=node22 \
  --outfile=dist/server.js

O comando lê o entry point, resolve imports analisáveis e produz um único arquivo. A opção platform=node ajusta resolução e mantém módulos nativos como node:fs externos.

Script no package.json

{
  "scripts": {
    "build": "node scripts/build.mjs",
    "typecheck": "tsc --noEmit"
  }
}

Veja npm Scripts no Node.js para organizar comandos previsíveis.

API JavaScript

import * as esbuild from 'esbuild';

await esbuild.build({
  entryPoints: ['src/server.ts'],
  bundle: true,
  platform: 'node',
  target: 'node22',
  format: 'esm',
  outfile: 'dist/server.js',
  sourcemap: true,
  logLevel: 'info'
});

A API oficial do esbuild documenta opções de build, transform, loaders, plugins, metafile e context. Para scripts mais complexos, a API evita problemas de escaping específicos do shell.

ESM no output

Para projetos modernos:

{
  "type": "module"
}
await esbuild.build({
  entryPoints: ['src/server.ts'],
  bundle: true,
  platform: 'node',
  format: 'esm',
  target: 'node22',
  outfile: 'dist/server.js'
});

O artigo TypeScript ESM no Node.js explica NodeNext, extensões e comportamento do runtime.

CommonJS

await esbuild.build({
  entryPoints: ['src/server.ts'],
  bundle: true,
  platform: 'node',
  format: 'cjs',
  target: 'node20',
  outfile: 'dist/server.cjs'
});

Use CommonJS apenas quando consumidores ou ferramentas exigem esse formato. Publicar ESM e CJS simultaneamente aumenta testes e risco de dual package hazard.

Bundling para aplicações

Aplicações podem se beneficiar de um artefato menor e com menos arquivos. Porém, nem toda dependência deve ser incorporada. Bibliotecas com addons nativos, arquivos dinâmicos ou resolução em runtime podem falhar quando empacotadas.

await esbuild.build({
  entryPoints: ['src/server.ts'],
  bundle: true,
  packages: 'external',
  platform: 'node',
  format: 'esm',
  outdir: 'dist'
});

packages: external mantém pacotes npm fora do bundle. O deploy precisa instalar as dependências de produção.

Dependências externas seletivas

external: [
  'pg-native',
  'sharp',
  '@aws-sdk/*'
]

Use quando apenas alguns pacotes precisam permanecer externos. Teste o artefato em ambiente limpo para confirmar que todos estão disponíveis.

Módulos nativos

Com platform: node, imports como node:fs, node:path e node:crypto são tratados como externos. Isso preserva o uso das APIs do runtime.

TypeScript sem typecheck

esbuild remove anotações de tipos rapidamente, mas não valida todo o programa:

const value: number = 'texto';

O build pode transformar esse arquivo. Mantenha:

npm run typecheck

O CI deve executar typecheck antes ou em paralelo com o build. Veja CI para Node.js com GitHub Actions.

tsconfig

esbuild descobre tsconfig.json e respeita opções relevantes de transformação. Ele não implementa todas as verificações do compilador. Evite assumir que uma opção de emissão do TypeScript terá efeito idêntico.

tsconfig: 'tsconfig.build.json'

Path aliases

Mappings de paths podem ser resolvidos durante o bundle. Isso funciona no artefato empacotado, mas pode divergir de uma publicação sem bundle. Para bibliotecas, prefira imports por nome real de pacote ou imports do package.json.

Sourcemaps

sourcemap: 'external',
sourcesContent: false

Sourcemaps ajudam stacks a apontar para TypeScript. Em produção, avalie se o conteúdo-fonte deve ser incluído e proteja os arquivos.

node --enable-source-maps dist/server.js

Minificação

minify: true,
keepNames: true

Minificar aplicações backend geralmente oferece benefício menor do que no navegador. Pode dificultar diagnóstico e alterar nomes usados por frameworks ou decorators. Meça tamanho, startup e qualidade das stacks antes de habilitar.

Define

define: {
  __BUILD_VERSION__: JSON.stringify(process.env.APP_VERSION ?? 'dev')
}

Use apenas valores não sensíveis. Tudo definido no bundle pode ser inspecionado no artefato.

Removendo logs

drop: ['debugger']

Remover console globalmente pode apagar logs operacionais importantes. Prefira níveis de logger configuráveis.

Múltiplos entry points

entryPoints: [
  'src/server.ts',
  'src/worker.ts',
  'src/cli.ts'
],
outdir: 'dist',
outbase: 'src'

Cada entry point gera um arquivo correspondente. Esse modelo é útil para API, workers e comandos administrativos.

Code splitting

splitting: true,
format: 'esm',
outdir: 'dist'

Code splitting exige ESM e múltiplos arquivos. Em backend, avalie se compartilhar chunks compensa a complexidade de deploy.

Assets

loader: {
  '.sql': 'text',
  '.graphql': 'text',
  '.wasm': 'file'
}

Loaders permitem incorporar texto ou copiar arquivos. Não use para incluir segredos ou configurações específicas de ambiente.

Watch mode com context

const context = await esbuild.context({
  entryPoints: ['src/server.ts'],
  bundle: true,
  platform: 'node',
  format: 'esm',
  outdir: 'dist'
});

await context.watch();

Ao encerrar:

await context.dispose();

Para reiniciar processos backend após o build, combine com um watcher externo ou o Watch Mode no Node.js.

Rebuild manual

const context = await esbuild.context(options);
await context.rebuild();
await context.dispose();

Rebuild reutiliza trabalho anterior e é útil em ferramentas próprias.

Plugins

const virtualPlugin = {
  name: 'virtual-config',
  setup(build) {
    build.onResolve({ filter: /^virtual:config$/ }, () => ({
      path: 'config',
      namespace: 'virtual'
    }));

    build.onLoad({ filter: /.*/, namespace: 'virtual' }, () => ({
      contents: `export default ${JSON.stringify({ mode: 'production' })}`,
      loader: 'js'
    }));
  }
};

Plugins executam código durante o build. Avalie manutenção e supply chain antes de adicionar plugins de terceiros.

Metafile

const result = await esbuild.build({
  ...options,
  metafile: true
});

await fs.writeFile(
  'dist/meta.json',
  JSON.stringify(result.metafile, null, 2)
);

O metafile mostra inputs, outputs, tamanhos e dependências. Use para detectar pacotes grandes ou inclusões inesperadas.

Analisando o bundle

const report = await esbuild.analyzeMetafile(result.metafile, {
  verbose: true
});

console.log(report);

Bibliotecas npm

Para bibliotecas, muitas vezes é melhor preservar módulos em vez de gerar um único bundle. Consumidores e bundlers conseguem aplicar tree shaking e resolver peer dependencies.

Se decidir bundlar:

  • marque peer dependencies como externas;
  • gere declarations com tsc;
  • teste ESM e CJS;
  • confirme exports;
  • inspecione o tarball.

Veja Publicar Pacote no npm.

Docker

FROM node:22 AS build
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run typecheck && npm run build

FROM node:22-slim
WORKDIR /app
COPY --from=build /app/dist ./dist
COPY package*.json ./
RUN npm ci --omit=dev
CMD ["node", "--enable-source-maps", "dist/server.js"]

Se todas as dependências estiverem incorporadas e não houver pacotes externos, a imagem final pode não precisar de node_modules. Confirme com testes reais.

CI

- run: npm ci
- run: npm run lint
- run: npm run typecheck
- run: npm test
- run: npm run build
- run: node dist/smoke-test.js

Armazene metafile e sourcemaps como artifacts protegidos quando úteis.

Segurança

  • Fixe a versão do esbuild.
  • Revise plugins.
  • Não incorpore segredos com define.
  • Não exponha sourcemaps publicamente sem necessidade.
  • Teste pacotes nativos.
  • Execute o artefato em ambiente limpo.
  • Use lockfile e npm ci.

Erros comuns

  • Confundir transpile com typecheck: erros de tipo passam.
  • Bundlar addon nativo: runtime não encontra binário.
  • Esquecer external: pacote dinâmico quebra.
  • Minificar sem testar: stacks e nomes ficam ruins.
  • Paths só no editor: publicação diverge.
  • Não executar dist: CI valida apenas fontes.
  • Plugin não confiável: build acessa credenciais.
  • Target incorreto: sintaxe não corresponde ao Node.js usado.

Configuração recomendada

import * as esbuild from 'esbuild';

await esbuild.build({
  entryPoints: ['src/server.ts'],
  bundle: true,
  packages: 'external',
  platform: 'node',
  target: 'node22',
  format: 'esm',
  outfile: 'dist/server.js',
  sourcemap: 'external',
  metafile: true,
  logLevel: 'info'
});

Conclusão

O esbuild no Node.js oferece builds rápidos e uma API simples para aplicações, CLIs e ferramentas. Com platform: node, target explícito, dependências externas e sourcemaps, ele produz artefatos previsíveis sem exigir uma configuração extensa.

Mantenha typecheck, testes e smoke test do output. Quando bundles, plugins e assets são tratados conscientemente, esbuild acelera o desenvolvimento sem esconder incompatibilidades que só apareceriam em produção.

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