# Como validar CPF no servidor usando Next.js API Routes

> Aprenda a validar CPF no servidor usando Next.js API Routes com validação algorítmica e consulta à API do CPFHub.

**Publicado:** 03/12/2024
**Autor:** Redação CPFHub.io
**URL:** https://cpfhub.io/blog/como-validar-cpf-servidor-nextjs-api-routes

---


Validar CPF no servidor com Next.js API Routes garante que somente documentos algoritmicamente corretos e presentes na base da Receita Federal sejam aceitos pelo sistema. A validação no servidor — e não apenas no cliente — protege contra manipulação de requisições e evita chamadas desnecessárias à API externa. O fluxo combina validação local de dígitos verificadores com consulta ao endpoint `GET https://api.cpfhub.io/cpf/{CPF}` usando o header `x-api-key`.

## Introdução

Validar CPF exclusivamente no cliente é insuficiente para garantir a integridade dos dados. A validação no servidor protege contra manipulação de requisições e garante que somente CPFs verificados sejam aceitos pelo sistema. Com o Next.js App Router, é possível criar Route Handlers que validam o CPF algoritmicamente e consultam a API do CPFHub.io para confirmação dos dados cadastrais.

---

## Criando o módulo de validação

Implemente a validação algorítmica do CPF em um módulo reutilizável:

```javascript
// lib/cpf-validator.js

export function validarDigitosCpf(cpf) {
 const cpfLimpo = cpf.replace(/\D/g, '');

 if (cpfLimpo.length !== 11) {
 return { valido: false, erro: 'CPF deve conter 11 dígitos' };
 }

 if (/^(\d)\1{10}$/.test(cpfLimpo)) {
 return { valido: false, erro: 'CPF com todos os dígitos iguais' };
 }

 const digitos = cpfLimpo.split('').map(Number);

 let soma = 0;
 for (let i = 0; i < 9; i++) {
 soma += digitos[i] * (10 - i);
 }
 const d1 = soma % 11 < 2 ? 0 : 11 - (soma % 11);

 soma = 0;
 for (let i = 0; i < 10; i++) {
 soma += digitos[i] * (11 - i);
 }
 const d2 = soma % 11 < 2 ? 0 : 11 - (soma % 11);

 if (digitos[9] !== d1 || digitos[10] !== d2) {
 return { valido: false, erro: 'Dígitos verificadores inválidos' };
 }

 return { valido: true, cpfLimpo };
}

export function formatarCpf(cpf) {
 const limpo = cpf.replace(/\D/g, '');
 return limpo.replace(/(\d{3})(\d{3})(\d{3})(\d{2})/, '$1.$2.$3-$4');
}
```

---

## Route Handler de validação

Crie o Route Handler que combina validação local com consulta à API:

```javascript
// app/api/validar-cpf/route.js

import { validarDigitosCpf } from '@/lib/cpf-validator';

export async function POST(request) {
 const body = await request.json();
 const { cpf } = body;

 if (!cpf) {
 return Response.json(
 { success: false, message: 'CPF é obrigatório' },
 { status: 400 }
 );
 }

 const validacaoLocal = validarDigitosCpf(cpf);

 if (!validacaoLocal.valido) {
 return Response.json({
 success: false,
 validacaoLocal: false,
 message: validacaoLocal.erro,
 }, { status: 400 });
 }

 try {
 const apiResponse = await fetch(
 `${process.env.CPFHUB_BASE_URL}/cpf/${validacaoLocal.cpfLimpo}`,
 {
 headers: {
 'x-api-key': process.env.CPFHUB_API_KEY,
 },
 }
 );

 if (!apiResponse.ok) {
 return Response.json({
 success: true,
 validacaoLocal: true,
 encontradoNaBase: false,
 message: 'CPF válido, porém não encontrado na base',
 });
 }

 const dados = await apiResponse.json();

 return Response.json({
 success: true,
 validacaoLocal: true,
 encontradoNaBase: dados.success,
 data: dados.success ? dados.data : null,
 message: dados.success
 ? 'CPF válido e encontrado'
 : 'CPF válido, mas sem dados',
 });
 } catch (error) {
 return Response.json({
 success: false,
 validacaoLocal: true,
 message: 'Erro ao consultar API externa',
 }, { status: 502 });
 }
}
```

---

## Route Handler para validação em lote

Crie um endpoint que aceita múltiplos CPFs para validação simultânea:

```javascript
// app/api/validar-cpf/lote/route.js

import { validarDigitosCpf } from '@/lib/cpf-validator';

export async function POST(request) {
 const { cpfs } = await request.json();

 if (!Array.isArray(cpfs) || cpfs.length === 0) {
 return Response.json(
 { success: false, message: 'Envie um array de CPFs' },
 { status: 400 }
 );
 }

 if (cpfs.length > 50) {
 return Response.json(
 { success: false, message: 'Máximo de 50 CPFs por requisição' },
 { status: 400 }
 );
 }

 const resultados = await Promise.allSettled(
 cpfs.map(async (cpf) => {
 const validacao = validarDigitosCpf(cpf);

 if (!validacao.valido) {
 return { cpf, valido: false, erro: validacao.erro };
 }

 const response = await fetch(
 `${process.env.CPFHUB_BASE_URL}/cpf/${validacao.cpfLimpo}`,
 {
 headers: { 'x-api-key': process.env.CPFHUB_API_KEY },
 }
 );

 if (!response.ok) {
 return { cpf, valido: true, encontrado: false };
 }

 const dados = await response.json();
 return {
 cpf,
 valido: true,
 encontrado: dados.success,
 nome: dados.success ? dados.data.name : null,
 };
 })
 );

 const processados = resultados.map((r) =>
 r.status === 'fulfilled' ? r.value : { erro: 'Falha no processamento' }
 );

 return Response.json({ success: true, resultados: processados });
}
```

| Endpoint | Método | Corpo | Descrição |
|---|---|---|---|
| `/api/validar-cpf` | POST | `{ "cpf": "123..." }` | Validação individual |
| `/api/validar-cpf/lote` | POST | `{ "cpfs": [...] }` | Validação em lote (max 50) |

---

## Middleware de rate limiting

Proteja os endpoints contra abuso implementando rate limiting:

```javascript
// middleware.js

import { NextResponse } from 'next/server';

const rateLimit = new Map();
const WINDOW_MS = 60 * 1000;
const MAX_REQUESTS = 30;

export function middleware(request) {
 if (!request.nextUrl.pathname.startsWith('/api/validar-cpf')) {
 return NextResponse.next();
 }

 const ip = request.headers.get('x-forwarded-for') ||
 request.headers.get('x-real-ip') || 'unknown';

 const now = Date.now();
 const windowStart = now - WINDOW_MS;

 if (!rateLimit.has(ip)) {
 rateLimit.set(ip, []);
 }

 const requests = rateLimit.get(ip).filter((t) => t > windowStart);
 requests.push(now);
 rateLimit.set(ip, requests);

 if (requests.length > MAX_REQUESTS) {
 return Response.json(
 { success: false, message: 'Limite de requisições excedido' },
 { status: 429, headers: { 'Retry-After': '60' } }
 );
 }

 return NextResponse.next();
}

export const config = {
 matcher: '/api/validar-cpf/:path*',
};
```

---

## Consumindo a API do lado do cliente

Crie um hook personalizado para facilitar o uso nos componentes React:

```javascript
// hooks/useCpfValidation.js
'use client';

import { useState, useCallback } from 'react';

export function useCpfValidation() {
 const [loading, setLoading] = useState(false);
 const [resultado, setResultado] = useState(null);
 const [erro, setErro] = useState(null);

 const validar = useCallback(async (cpf) => {
 setLoading(true);
 setErro(null);
 setResultado(null);

 try {
 const response = await fetch('/api/validar-cpf', {
 method: 'POST',
 headers: { 'Content-Type': 'application/json' },
 body: JSON.stringify({ cpf }),
 });

 const data = await response.json();

 if (response.ok && data.success) {
 setResultado(data);
 } else {
 setErro(data.message || 'Erro na validação');
 }
 } catch {
 setErro('Erro de conexão');
 } finally {
 setLoading(false);
 }
 }, []);

 return { validar, loading, resultado, erro };
}
```

---

## Perguntas frequentes

### Por que validar o CPF no servidor e não apenas no cliente em Next.js?

A validação no cliente pode ser burlada por qualquer pessoa com acesso às ferramentas de desenvolvedor do navegador: basta interceptar a requisição e enviar um CPF diferente do que foi validado. A validação no Route Handler — no servidor — garante que todo CPF processado pela sua lógica de negócio passou pela verificação, independente do que o cliente enviou.

### Qual a diferença entre a validação algorítmica e a consulta à API de CPF?

A validação algorítmica verifica se os dígitos verificadores são matematicamente corretos — ela detecta CPFs mal formatados ou fabricados, mas não confirma se o CPF pertence a uma pessoa real. A consulta à API da CPFHub.io acessa a base da Receita Federal e retorna nome, gênero e data de nascimento do titular, confirmando que o CPF existe e está ativo.

### Como o rate limiting no middleware protege o endpoint de validação?

O middleware limita cada IP a 30 requisições por minuto. Isso previne ataques de força bruta que tentam descobrir CPFs válidos enviando sequências numéricas em massa. O rate limiting acontece antes de qualquer lógica de negócio, reduzindo carga no servidor e nas chamadas à API externa. Para mais detalhes sobre middleware no Next.js, consulte a [documentação oficial do Next.js](https://nextjs.org/docs/app/building-your-application/routing/middleware).

### A API da CPFHub.io retorna erro 429 quando o limite do plano é atingido?

Não. A API da CPFHub.io nunca retorna HTTP 429 nem bloqueia requisições por limite de plano. Ao ultrapassar o limite mensal (50 consultas no plano gratuito ou 1.000 no Pro), as consultas adicionais são cobradas automaticamente a R$0,15 cada — o fluxo de validação continua sem interrupções.

### Leia também

- [Como validar CPF no frontend com React e API REST](https://cpfhub.io/blog/como-validar-cpf-no-frontend-com-react-e-api-rest)
- [SLA de API de CPF: níveis de disponibilidade e o que exigir do seu provedor](https://cpfhub.io/blog/sla-api-cpf-niveis-disponibilidade)
- [API de CPF grátis para desenvolvedores: como começar em 5 minutos](https://cpfhub.io/blog/api-cpf-gratis-desenvolvedores-comecar-5-minutos)
- [10 erros mais comuns ao integrar uma API de CPF e como evitá-los](https://cpfhub.io/blog/10-erros-mais-comuns-ao-integrar-uma-api-de-cpf)

---

## Conclusão

Validar CPF no servidor com Next.js API Routes garante segurança e integridade dos dados. A chave de API fica protegida em variáveis de ambiente, a validação algorítmica evita chamadas desnecessárias à API externa e o rate limiting previne abuso. A combinação dos dois níveis de validação — local e via API — cobre tanto CPFs matematicamente inválidos quanto documentos que não existem na base da Receita Federal.

Cadastre-se em [cpfhub.io](https://www.cpfhub.io/) — 50 consultas mensais gratuitas, sem cartão de crédito — e implemente validação de CPF no servidor do seu projeto Next.js hoje mesmo.

