DEV Community

Marcelo Matz
Marcelo Matz

Posted on

Criando um Serviço de Busca de CEP Simples com Go

Neste artigo, faremos uma imersão em um serviço básico escrito em Go que pode servir para buscar informações de um CEP (Código de Endereçamento Postal) no Brasil.

Vamos usar a API ViaCep para busca de CEPs brasileiros.

Estrutura do Código

O código é dividido em três partes principais:

  1. A definição do tipo ViaCep, que é uma estrutura (struct) que espelha os dados retornados pela API ViaCep.

  2. A função main, que inicia um servidor HTTP e define o roteamento.

  3. As funções BuscaCepHandle e BuscaCep que lidam com a lógica principal da aplicação.

Definindo o tipo ViaCep

Vamos definir o tipo ViaCep como uma estrutura com vários campos do tipo string. Esses campos refletem as propriedades do objeto JSON que recebemos da API ViaCep.

type ViaCep struct {
    Cep         string `json:"cep"`
    Logradouro  string `json:"logradouro"`
    Complemento string `json:"complemento"`
    Bairro      string `json:"bairro"`
    Localidade  string `json:"localidade"`
    Uf          string `json:"uf"`
    Ibge        string `json:"ibge"`
    Gia         string `json:"gia"`
    Ddd         string `json:"ddd"`
    Siafi       string `json:"siafi"`
}
Enter fullscreen mode Exit fullscreen mode

As tags json:"..." anexadas a cada campo da struct fazem o rolê de instruir o pacote encoding/json sobre como fazer o mapeamento do JSON para a struct em Go.

Função Main

Na função main começamos escutando na porta 8080 e configuramos um roteamento de URL simples, onde todas as requisições para a rota / são tratadas pela função BuscaCepHandle.

func main() {
    http.HandleFunc("/", BuscaCepHandle)
    err := http.ListenAndServe(":8080", nil)
    if err != nil {
    return
    }
}
Enter fullscreen mode Exit fullscreen mode

Se um erro ocorrer durante http.ListenAndServe, nós simplesmente retornamos e encerramos a execução do programa.

Funções BuscaCepHandle e BuscaCep

A função BuscaCepHandle é nossa manipuladora de requisições HTTP. Ela lida com todas as solicitações que chegam à raíz ("/") da nossa aplicação.

func BuscaCepHandle(w http.ResponseWriter, r *http.Request) {
    if r.URL.Path != "/" {
        w.WriteHeader(http.StatusNotFound)
        return
    }
    cepParam := r.URL.Query().Get("cep")
    if cepParam == "" || len(cepParam) != 8 {
        w.WriteHeader(http.StatusBadRequest)
        return
    }
    // ... (código truncado por brevidade)
}
Enter fullscreen mode Exit fullscreen mode

Esta função verifica se o parâmetro do cep foi passado na requisição e se ele possui 8 caracteres. Se o CEP não for válido, devolvemos um status HTTP 400.

A função BuscaCep é uma função auxiliar usada por BuscaCepHandle para fazer a solicitação real para a API ViaCep e processar os dados de resposta.

func BuscaCep(cep string) (*ViaCep, error) {
    url := "https://viacep.com.br/ws/" + cep + "/json/"
    resp, err := http.Get(url)
    if err != nil {
        return nil, err
    }
    defer resp.Body.Close()
    body, err := io.ReadAll(resp.Body)
    if err != nil {
        return nil, err
    }
    var viaCep ViaCep
    err = json.Unmarshal(body, &viaCep)
    if err != nil {
        return nil, err
    }
    return &viaCep, nil
}
Enter fullscreen mode Exit fullscreen mode

Esta função faz uma requisição para a API ViaCep, lê a resposta e a deserializa em um objeto ViaCep.

A função retorna um ponteiro para um objeto ViaCep ou um erro, caso algo dê errado durante a solicitação HTTP ou a deserialização do JSON da resposta.

Abaixo eu deixo um exemplo do código completo para você analisar:

package main

import (
    "encoding/json"
    "io"
    "net/http"
)

type ViaCep struct {
    Cep         string `json:"cep"`
    Logradouro  string `json:"logradouro"`
    Complemento string `json:"complemento"`
    Bairro      string `json:"bairro"`
    Localidade  string `json:"localidade"`
    Uf          string `json:"uf"`
    Ibge        string `json:"ibge"`
    Gia         string `json:"gia"`
    Ddd         string `json:"ddd"`
    Siafi       string `json:"siafi"`
}

func main() {
    http.HandleFunc("/", BuscaCepHandle)
    err := http.ListenAndServe(":8080", nil)
    if err != nil {
        return
    }
}

func BuscaCepHandle(w http.ResponseWriter, r *http.Request) {
    if r.URL.Path != "/" {
        w.WriteHeader(http.StatusNotFound)
        return
    }
    cepParam := r.URL.Query().Get("cep")
    if cepParam == "" || len(cepParam) != 8 {
        w.WriteHeader(http.StatusBadRequest)
        return
    }
    w.Header().Set("Content-Type", "application/json")
    w.WriteHeader(http.StatusOK)
    viaCep, err := BuscaCep(cepParam)
    if err != nil {
        w.WriteHeader(http.StatusInternalServerError)
        return
    }
    err = json.NewEncoder(w).Encode(viaCep)
    if err != nil {
        return
    }

}

func BuscaCep(cep string) (*ViaCep, error) {
    url := "https://viacep.com.br/ws/" + cep + "/json/"
    resp, err := http.Get(url)
    if err != nil {
        return nil, err
    }
    defer resp.Body.Close()
    body, err := io.ReadAll(resp.Body)
    if err != nil {
        return nil, err
    }
    var viaCep ViaCep
    err = json.Unmarshal(body, &viaCep)
    if err != nil {
        return nil, err
    }
    return &viaCep, nil
}

Enter fullscreen mode Exit fullscreen mode

Pronto!
Isso é o suficiente para criar um microsserviço simples de busca de CEP usando Go.

Se você quiser ter acesso ao código completo, pode acessar o repositório do projeto no GitHub.

Embora seja uma aplicação bastante simples, ela forma a base de como um serviço da vida real pode ser construído utilizando Go para buscar e fornecer informações do CEP a partir de uma API.

Melhorias

Apesar de este ser um código que funciona e retorna o dado que esperamos, existem algumas coisas que podem ser melhoradas neste código.

Tratamento de erros: Quando um erro ocorrer, pode ser interessante retornar a mensagem de erro real ao invés de apenas um código de status HTTP para ajudar na depuração.

Lembre-se de que esta não é uma boa prática para um ambiente de produção, pois pode expor detalhes do sistema a usuários indesejados.

Em um ambiente de produção, uma coisa que você pode fazer é logar o erro e retornar um ID de log para o cliente como uma referência para ele usar na abertura de um atendimento de suporte, por exemplo.

Endpoint do servidor: Atualmente o serviço está configurado para escutar todas as requisições na raiz ("/").

Poderia ser mais inteligente e útil ter um endpoint específico para essa funcionalidade como "/ceps" ou "/buscarCep". O exemplo não considerou isso justamente por ser um exemplo!

Validação do CEP: Poderia ser interessante adicionar uma validação mais forte para o campo 'cep'.

Atualmente, o código apenas verifica se tem 8 caracteres, mas pode existir um cenário onde a entrada tenha 8 caracteres mas não seja um CEP válido. 🤡

Testes: Incluir testes de unidade e integração para garantir que o serviço está funcionando conforme esperado.

Na verdade tudo isso deveria ter começado a partir da escrita de testes. My bad

Goroutine no BuscaCep: A função do BuscaCep que chama um serviço externo poderia considerar utilizar uma goroutine para executar essa tarefa assincronamente.

Verificações HTTP: A resposta da função http.Get(url) pode vir com statusCode não 200. Seria inteligente fazer essa verificação antes de ler o corpo da resposta.


Agora sim, pronto!

Talvez quando você ler este post, os arquivos no repositório já tenham sido alterados e algumas dessas (ou todas) melhorias implementadas.

E se você tiver sugestões que colaborem com o aprendizado de outras pessoas sobre este mesmo assunto, fique a vontade para fazer um pull-request no GitHub.

Até a próxima 🤘

Top comments (0)