Integracao ViaCEP

Service de consulta de enderecos brasileiros por CEP.

A integracao ViaCEP gera um service para consultar enderecos brasileiros a partir de um CEP. Usa a API publica viacep.com.br com cache Redis opcional.

ViaCEP Service

// apps/api/src/integrations/viacep/viacep.service.ts
export interface ViaCepAddress {
  cep: string;
  logradouro: string;
  complemento: string;
  bairro: string;
  localidade: string;
  uf: string;
  ibge: string;
}

export class ViaCepService {
  private baseUrl = 'https://viacep.com.br/ws';

  constructor(private redis?: RedisService) {}

  async findByCep(cep: string): Promise<ViaCepAddress> {
    const cleaned = cep.replace(/\D/g, '');
    if (cleaned.length !== 8) {
      throw new Error('CEP deve ter 8 digitos');
    }

    // Cache (se Redis disponivel)
    if (this.redis) {
      const cached = await this.redis.get(`cep:${cleaned}`);
      if (cached) return JSON.parse(cached);
    }

    const response = await fetch(`${this.baseUrl}/${cleaned}/json/`);
    const data = await response.json();

    if (data.erro) {
      throw new Error('CEP nao encontrado');
    }

    // Cachear por 30 dias
    if (this.redis) {
      await this.redis.set(`cep:${cleaned}`, JSON.stringify(data), 'EX', 2592000);
    }

    return data;
  }
}

Controller / Rota

// apps/api/src/integrations/viacep/viacep.controller.ts

// NestJS:
@Controller('api/cep')
export class ViaCepController {
  constructor(private viaCepService: ViaCepService) {}

  @Get(':cep')
  async findByCep(@Param('cep') cep: string) {
    return this.viaCepService.findByCep(cep);
  }
}

// Express / Fastify:
app.get('/api/cep/:cep', async (req, res) => {
  try {
    const address = await viaCepService.findByCep(req.params.cep);
    res.json(address);
  } catch (error) {
    res.status(400).json({ error: error.message });
  }
});

Uso no frontend

// apps/web/src/hooks/useCep.ts
import { useState } from 'react';

export function useCep() {
  const [address, setAddress] = useState(null);
  const [loading, setLoading] = useState(false);

  async function fetchCep(cep: string) {
    if (cep.replace(/\D/g, '').length !== 8) return;
    setLoading(true);
    try {
      const res = await fetch(`/api/cep/${cep}`);
      const data = await res.json();
      setAddress(data);
    } catch {
      setAddress(null);
    } finally {
      setLoading(false);
    }
  }

  return { address, loading, fetchCep };
}

// Em um formulario:
function AddressForm() {
  const { address, loading, fetchCep } = useCep();

  return (
    <div>
      <input
        placeholder="CEP"
        onBlur={(e) => fetchCep(e.target.value)}
      />
      {address && (
        <>
          <input value={address.logradouro} readOnly />
          <input value={address.bairro} readOnly />
          <input value={address.localidade} readOnly />
          <input value={address.uf} readOnly />
        </>
      )}
    </div>
  );
}