← Voltar ao blog

Validação de CNPJ em Node.js: implementação completa sem dependências

·7 min de leitura

Se você está construindo um sistema que aceita CNPJ (cadastro de fornecedores, marketplace, emissão de NF-e), precisa validar o formato e os dígitos verificadores. Muitos devs instalam uma lib para isso, mas a validação é simples o suficiente para implementar em poucas linhas.

Estrutura do CNPJ

O CNPJ tem 14 dígitos no formato XX.XXX.XXX/XXXX-XX. Os 8 primeiros identificam a empresa, os 4 seguintes identificam a filial (0001 para matriz), e os 2 últimos são dígitos verificadores calculados pelo algoritmo módulo 11.

O algoritmo

O cálculo é similar ao do CPF, mas com pesos diferentes:

  1. Remova a formatação (pontos, barra, traço)
  2. Verifique se tem exatamente 14 dígitos e não é uma sequência repetida
  3. Para o primeiro dígito verificador: multiplique os 12 primeiros dígitos pelos pesos 5,4,3,2,9,8,7,6,5,4,3,2
  4. Some os resultados e calcule o resto da divisão por 11
  5. Se o resto for menor que 2, o dígito é 0; caso contrário, é 11 menos o resto
  6. Repita com os 13 dígitos e pesos 6,5,4,3,2,9,8,7,6,5,4,3,2 para o segundo dígito

Implementação em TypeScript

function validateCNPJ(cnpj: string): boolean {
// Remove formatação
const digits = cnpj.replace(/\D/g, '');
// Deve ter 14 dígitos
if (digits.length !== 14) return false;
// Rejeita sequências repetidas
if (/^(\d)\1+$/.test(digits)) return false;
// Calcula dígitos verificadores
const weights1 = [5,4,3,2,9,8,7,6,5,4,3,2];
const weights2 = [6,5,4,3,2,9,8,7,6,5,4,3,2];
const calcDigit = (slice: string, w: number[]) => {
const sum = slice.split('')
.reduce((acc, d, i) => acc + parseInt(d) * w[i], 0);
const rest = sum % 11;
return rest < 2 ? 0 : 11 - rest;
};
const d1 = calcDigit(digits.slice(0, 12), weights1);
const d2 = calcDigit(digits.slice(0, 12) + d1, weights2);
return d1 === parseInt(digits[12])
&& d2 === parseInt(digits[13]);
}

Testando a validação

Agora você precisa de CNPJs fictícios para testar. Se usar apenas CNPJs inválidos, você só testa o caminho de rejeição. Gere CNPJs válidos para testar o caminho de aceitação:

CNPJs fictícios com dígitos verificadores válidos

Clique em Gerar para começar

Casos de teste recomendados

// Deve aceitar
validateCNPJ('11.222.333/0001-81'); // formatado
validateCNPJ('11222333000181'); // sem formato
// Deve rejeitar
validateCNPJ('11.111.111/1111-11'); // sequência repetida
validateCNPJ('12.345.678/0001-00'); // dígitos errados
validateCNPJ('123'); // tamanho errado
validateCNPJ(''); // vazio

Via API para testes em massa

# Gerar 200 CNPJs para popular o banco de testes (chave do plano Dev)
curl -H "X-API-Key: $FAKEFORGE_API_KEY" "https://fakeforge.com.br/api/generate?type=cnpj&quantity=200&format=json"

Edge cases que quebram validadores simples

A função acima cobre o caminho feliz, mas CNPJ em produção chega sujo. Vale testar estes casos antes de publicar:

  • Tipo errado: o campo pode chegar como número, null ou undefined. Um CNPJ com zeros à esquerda perde dígitos quando vira number (00.000.000/0001-91 vira 191). Aceite só string e recuse o resto.
  • Espaços e caracteres invisíveis: copiar de PDF ou planilha traz espaços não separáveis. A limpeza com regex remove, mas confira o tamanho depois dela.
  • Zeros à esquerda: use padStart(14, "0") só se a origem for um campo numérico legado. Em API pública, exija os 14 dígitos.
  • Sequências repetidas: 00000000000000 passa no cálculo do mod-11, por isso a checagem de repetição é obrigatória.
  • Matriz e filial: a ordem (0001, 0002...) não altera o algoritmo. Os dígitos verificadores consideram os 12 primeiros dígitos inteiros.
  • Validade matemática não é existência: um CNPJ pode passar no mod-11 e não existir na Receita Federal. Para saber se a empresa está ativa, consulte uma fonte oficial.

CNPJ alfanumérico a partir de 2026

A Receita Federal passou a emitir CNPJs com letras nas 12 primeiras posições. Os dois últimos caracteres continuam numéricos. O cálculo usa o mesmo mod-11 e os mesmos pesos, mas cada caractere entra como seu código ASCII menos 48 (o 0 vale 0, o A vale 17, o Z vale 42). Se o seu validador limpa a entrada com um regex de "só dígitos", ele apaga as letras e rejeita CNPJs legítimos. Troque por /[^0-9A-Z]/gi e converta para maiúsculas antes de calcular. Há um passo a passo no gerador de CNPJ alfanumérico.

Usando a validação em Express

Em Express, valide na borda da rota e devolva 422 com mensagem clara. Normalize o CNPJ antes de gravar, para que o índice único no banco não aceite duas versões do mesmo número.

import express from "express";
import { validateCNPJ } from "./validate-cnpj";

const app = express();
app.use(express.json());

app.post("/fornecedores", (req, res) => {
  const { cnpj } = req.body ?? {};
  if (typeof cnpj !== "string" || !validateCNPJ(cnpj)) {
    return res.status(422).json({ error: "CNPJ inválido" });
  }
  // normaliza antes de salvar
  const normalizado = cnpj.replace(/\D/g, "");
  return res.status(201).json({ cnpj: normalizado });
});

Usando a validação em NestJS

No NestJS o caminho natural é um decorator do class-validator. Assim o ValidationPipe global rejeita o DTO antes de chegar ao service.

import { registerDecorator, ValidationOptions } from "class-validator";
import { validateCNPJ } from "./validate-cnpj";

export function IsCNPJ(options?: ValidationOptions) {
  return (object: object, propertyName: string) => {
    registerDecorator({
      name: "isCNPJ",
      target: object.constructor,
      propertyName,
      options: { message: "CNPJ inválido", ...options },
      validator: {
        validate: (value: unknown) => typeof value === "string" && validateCNPJ(value),
      },
    });
  };
}

// create-fornecedor.dto.ts
export class CreateFornecedorDto {
  @IsCNPJ()
  cnpj: string;
}

Usando a validação em Fastify

No Fastify, o JSON Schema cuida do formato e do tamanho, e um hook preValidation aplica o dígito verificador. O schema barra lixo barato antes do cálculo.

import Fastify from "fastify";
import { validateCNPJ } from "./validate-cnpj";

const app = Fastify();

app.post("/fornecedores", {
  schema: {
    body: {
      type: "object",
      required: ["cnpj"],
      properties: { cnpj: { type: "string", minLength: 14, maxLength: 18 } },
    },
  },
  preValidation: async (req, reply) => {
    const { cnpj } = req.body as { cnpj: string };
    if (!validateCNPJ(cnpj)) {
      return reply.code(422).send({ error: "CNPJ inválido" });
    }
  },
}, async () => ({ ok: true }));

Perguntas frequentes

Preciso de uma biblioteca para validar CNPJ?

Não. São cerca de 20 linhas e você controla o comportamento com entradas estranhas. Uma dependência só compensa se o time precisar de CPF, CNPJ, título de eleitor e outros documentos com a mesma API.

Posso usar CNPJs gerados em ambiente de teste?

Sim, para validar formato e fluxo. Um CNPJ gerado passa no mod-11, mas pode coincidir com o de uma empresa real por acaso. Nunca use esses números para emitir nota fiscal ou chamar serviços oficiais.

O validador deve aceitar CNPJ formatado?

Aceite as duas formas na entrada e grave sempre só os 14 caracteres. Formate apenas na exibição.

Como testar o validador de forma automatizada?

Gere uma lista de CNPJs válidos pela API e rode todos no teste de aceitação. Depois altere um dígito de cada um e confirme que todos são rejeitados. Isso pega erros de peso e de resto que um único exemplo manual não pega.

Resumo

  • Validação de CNPJ são ~20 linhas de código, sem necessidade de libs externas
  • O algoritmo mod-11 com pesos específicos valida os 2 dígitos verificadores
  • Teste com CNPJs válidos (gerados) e inválidos para cobrir ambos os caminhos
  • Use o validador online para checar CNPJs rapidamente
  • Trate tipo, zeros à esquerda e CNPJ alfanumérico antes de confiar no cálculo
  • Use a API para gerar CNPJs em massa no CI/CD

Achou útil? Compartilha: