Como validar CPF em aplicações Nuxt.js com server middleware

Aprenda a integrar a validação de CPF via API em aplicações Nuxt.js usando server middleware para proteger sua chave de API no back-end.

Redação CPFHub.io
Redação CPFHub.io
··8 min de leitura
Como validar CPF em aplicações Nuxt.js com server middleware

Para validar CPF em aplicações Nuxt.js sem expor a chave de API no cliente, crie uma server route em server/api/cpf/[cpf].ts que recebe o CPF do front-end, faz a chamada à API CPFHub.io com o header x-api-key a partir de uma variável de ambiente e devolve o resultado ao navegador. Dessa forma, o segredo nunca aparece no bundle JavaScript enviado ao usuário.

Introdução

O Nuxt.js é um dos frameworks mais populares para construção de aplicações Vue.js com renderização server-side (SSR) e geração estática. Quando se trata de integrar APIs externas que exigem autenticação, como a API de consulta de CPF, é fundamental que a chave de API nunca seja exposta no lado do cliente.

A abordagem recomendada em Nuxt.js é utilizar server middleware (Nuxt 3) ou server routes para criar um proxy que faz a chamada à API no lado do servidor.


Por que usar server middleware para consultas de CPF

A chave de API (x-api-key) é um segredo que não deve ser incluído em código JavaScript enviado ao navegador. Se exposta, qualquer pessoa poderia utilizá-la para consumir sua cota de consultas.

O server middleware do Nuxt.js resolve esse problema criando uma rota no servidor que atua como intermediária entre o front-end e a API externa. O fluxo funciona assim:

  1. O front-end faz uma requisição para uma rota interna do Nuxt (por exemplo, /api/cpf/12345678900).
  2. O server middleware recebe essa requisição e faz a chamada à API da CPFHub.io com a chave de API armazenada no ambiente do servidor.
  3. O resultado é devolvido ao front-end sem que a chave de API seja exposta.

Configuração do projeto

Estrutura de diretórios (Nuxt 3)

meu-projeto/
    server/
    api/
    cpf/
    [cpf].ts
    pages/
    index.vue
    .env
    nuxt.config.ts

Variáveis de ambiente

Crie um arquivo .env na raiz do projeto:

CPFHUB_API_KEY=SUA_CHAVE_DE_API

No nuxt.config.ts, configure o acesso à variável de ambiente:

export default defineNuxtConfig({
    runtimeConfig: {
    cpfhubApiKey: process.env.CPFHUB_API_KEY || ''
    }
})

A propriedade runtimeConfig sem o prefixo public garante que a variável esteja disponível apenas no lado do servidor.


Criando a server route

Crie o arquivo server/api/cpf/[cpf].ts:

import { defineEventHandler, createError } from 'h3'

export default defineEventHandler(async (event) => {
    const cpf = event.context.params?.cpf

    if (!cpf || cpf.length !== 11 || !/^\d{11}$/.test(cpf)) {
    throw createError({
    statusCode: 400,
    statusMessage: 'CPF inválido. Informe 11 dígitos numéricos.'
    })
    }

    const config = useRuntimeConfig()
    const apiKey = config.cpfhubApiKey

    if (!apiKey) {
    throw createError({
    statusCode: 500,
    statusMessage: 'Chave de API não configurada no servidor.'
    })
    }

    const controller = new AbortController()
    const timeoutId = setTimeout(() => controller.abort(), 10000)

    try {
    const response = await fetch(
    `https://api.cpfhub.io/cpf/${cpf}`,
    {
    method: 'GET',
    headers: {
    'x-api-key': apiKey,
    'Accept': 'application/json'
    },
    signal: controller.signal
    }
    )

    clearTimeout(timeoutId)

    if (!response.ok) {
    if (response.status === 429) {
    throw createError({
    statusCode: 429,
    statusMessage: 'Limite de consultas atingido. Tente novamente mais tarde.'
    })
    }
    throw createError({
    statusCode: response.status,
    statusMessage: `Erro na API: ${response.statusText}`
    })
    }

    const dados = await response.json()
    return dados
    } catch (error: any) {
    clearTimeout(timeoutId)

    if (error.name === 'AbortError') {
    throw createError({
    statusCode: 504,
    statusMessage: 'Tempo limite excedido na consulta de CPF.'
    })
    }

    if (error.statusCode) {
    throw error
    }

    throw createError({
    statusCode: 500,
    statusMessage: 'Erro interno ao consultar CPF.'
    })
    }
})

Essa rota está acessível em http://localhost:3000/api/cpf/{cpf} durante o desenvolvimento e será automaticamente incluída no build de produção.


Consumindo a rota no front-end

Crie um componente Vue que permite ao usuário digitar um CPF e consultar os dados.

Página de consulta (pages/index.vue)

<template>
    <div class="container">
    <h1>Consulta de CPF</h1>
    <form @submit.prevent="consultarCpf">
    <input
    v-model="cpfInput"
    type="text"
    placeholder="Digite o CPF (somente números)"
    maxlength="11"
    />
    <button type="submit" :disabled="carregando">
    {{ carregando ? 'Consultando...' : 'Consultar' }}
    </button>
    </form>

    <div v-if="resultado" class="resultado">
    <p><strong>Nome:</strong> {{ resultado.data.name }}</p>
    <p><strong>CPF:</strong> {{ resultado.data.cpf }}</p>
    <p><strong>Data de nascimento:</strong> {{ resultado.data.birthDate }}</p>
    <p><strong>Gênero:</strong> {{ resultado.data.gender }}</p>
    </div>

    <div v-if="erro" class="erro">
    <p>{{ erro }}</p>
    </div>
    </div>
</template>

<script setup lang="ts">
import { ref } from 'vue'

const cpfInput = ref('')
const resultado = ref(null)
const erro = ref('')
const carregando = ref(false)

async function consultarCpf() {
    erro.value = ''
    resultado.value = null
    carregando.value = true

    const cpf = cpfInput.value.replace(/\D/g, '')

    if (cpf.length !== 11) {
    erro.value = 'Informe um CPF com 11 dígitos.'
    carregando.value = false
    return
    }

    try {
    const dados = await $fetch(`/api/cpf/${cpf}`)
    resultado.value = dados
    } catch (e: any) {
    erro.value = e.data?.message || 'Erro ao consultar CPF.'
    } finally {
    carregando.value = false
    }
}
</script>

Testando com cURL

Você pode testar a server route diretamente via cURL durante o desenvolvimento:

curl -X GET http://localhost:3000/api/cpf/12345678900 \
    -H "Accept: application/json" \
    --max-time 10

A resposta será o JSON retornado pela API da CPFHub.io, sem que a chave de API seja visível para o cliente.


Adicionando validação sintática no server middleware

Para evitar consultas desnecessárias à API, implemente a validação dos dígitos verificadores do CPF no próprio server middleware:

function validarDigitosCpf(cpf: string): boolean {
    if (cpf.length !== 11) return false
    if (/^(\d)\1{10}$/.test(cpf)) return false

    let soma = 0
    for (let i = 0; i < 9; i++) {
    soma += parseInt(cpf.charAt(i)) * (10 - i)
    }
    let resto = (soma * 10) % 11
    if (resto === 10) resto = 0
    if (resto !== parseInt(cpf.charAt(9))) return false

    soma = 0
    for (let i = 0; i < 10; i++) {
    soma += parseInt(cpf.charAt(i)) * (11 - i)
    }
    resto = (soma * 10) % 11
    if (resto === 10) resto = 0
    if (resto !== parseInt(cpf.charAt(10))) return false

    return true
}

Adicione essa verificação antes da chamada à API para rejeitar CPFs sintaticamente inválidos sem consumir uma consulta.


Boas práticas

  • Nunca exponha a chave de API -- Utilize runtimeConfig sem o prefixo public para manter segredos no servidor.

  • Timeout -- Sempre inclua timeout nas requisições para evitar que o servidor fique travado.

  • Validação no front-end -- Valide o formato do CPF (11 dígitos numéricos) antes de enviar ao servidor.

  • Validação no back-end -- Revalide no server middleware, pois o front-end pode ser contornado.

  • Rate limit -- No plano gratuito da CPFHub.io, o limite é de 1 requisição a cada 2 segundos, com 50 consultas/mês. O plano Pro oferece 1 requisição por segundo e 1.000 consultas/mês por R$ 149.


Perguntas frequentes

Por que usar server routes em vez de chamar a API diretamente do componente Vue?

Chamar a API CPFHub.io diretamente do componente Vue expõe a x-api-key no bundle JavaScript do navegador, onde qualquer usuário pode inspecioná-la. Com a server route, a chave fica em variável de ambiente no servidor e nunca trafega até o cliente — essa é a abordagem recomendada pelo OWASP para proteção de segredos em aplicações web.

Como o Nuxt.js lida com o runtimeConfig em produção?

Em produção, o runtimeConfig é preenchido pelas variáveis de ambiente do servidor no momento do deploy — no Vercel, Render ou qualquer outro provedor, você define CPFHUB_API_KEY nas configurações de environment. Os valores sem o prefixo public ficam acessíveis apenas em server/ e nunca são injetados no bundle do cliente.

O que acontece se a consulta de CPF exceder o limite do plano gratuito?

A API CPFHub.io não retorna erro bloqueante ao atingir o limite de 50 consultas mensais. Em vez disso, cada consulta adicional é cobrada a R$0,15. No server middleware, o status HTTP retornado será 200 normalmente — acompanhe o consumo no dashboard em app.cpfhub.io para evitar cobranças inesperadas.

Como adicionar cache nas server routes do Nuxt para economizar consultas?

Use o composable useStorage do Nitro (camada de servidor do Nuxt 3) para armazenar resultados em memória ou Redis. Defina um TTL adequado — por exemplo, 24 horas para dados cadastrais que raramente mudam — e verifique o cache antes de chamar a API. Isso reduz o consumo de consultas e a latência percebida pelo usuário.


Conclusão

Integrar a validação de CPF em aplicações Nuxt.js com server middleware é a abordagem mais segura e eficiente para proteger sua chave de API e garantir que as consultas sejam feitas de forma controlada. Com a API da CPFHub.io, o retorno inclui nome, gênero e data de nascimento do titular em uma única requisição GET.

Cadastre-se em cpfhub.io — 50 consultas mensais gratuitas, sem cartão de crédito — e implemente hoje mesmo a server route de validação de CPF na sua aplicação Nuxt.js.

CPFHub.io

Pronto para integrar a API?

50 consultas gratuitas para testar agora. Sem cartão de crédito. Acesso imediato à documentação.

Redação CPFHub.io

Sobre a redação

Redação CPFHub.io

Time editorial especializado em APIs de CPF, identidade digital e compliance no mercado brasileiro. Produzimos guias técnicos, análises regulatórias e tutoriais sobre LGPD e KYC para desenvolvedores e líderes de produto.

WhatsAppFale conosco via WhatsApp