Skip to content
← Voltar para o Skalablog

Artigo publicado

Endpoint para roteamento com Google Maps no Node.js: rotas salvas no MongoDB

Neste tutorial, você acompanha o desenvolvimento de um endpoint para criação de rotas usando a API do Google Maps. O processo cobre a configuração do ambiente, a camada de casos de uso, a implementação de testes unitários e a persistência no banco, para que motoristas e entregadores de aplicativo possam recuperar depois as rotas criadas.

O conteúdo é baseado na aula 87 do canal Gustavo Dev Doido, e reaproveita a estrutura da aula anterior, em que já tínhamos um método para buscar locais e obter direções com a biblioteca do Google Maps. A ideia agora é transformar aquelas funções em um fluxo completo de gravação.

O que o endpoint de criação de rotas faz

O endpoint recebe apenas dois dados do usuário: os pontos de partida e de chegada. A API do Google Maps resolve o resto, devolvendo as direções entre eles. O resultado é processado e salvo no banco, para que a rota possa ser reutilizada depois por quem quiser fazer o trajeto.

Antes de qualquer código, vale entender a divisão em camadas que a aula propõe:

  • Controller/endpoint: recebe a requisição HTTP e repassa os dados.
  • Caso de uso (use case): concentra a regra de negócio da criação de rota.
  • Factory: monta as dependências (adapters) que o caso de uso precisa.
  • Adapter do Google Maps: faz a chamada real à biblioteca e devolve as direções.
  • Repositório: salva a rota formatada no banco de dados.

Essa separação é o que permite testar a regra de negócio sem depender de chamadas reais à API. É o mesmo padrão explicado com mais calma no curso da Crazystack.

Configuração do ambiente e estrutura de arquivos

O primeiro passo é acessar a pasta map-route, criada na aula anterior, onde já ficaram os arquivos de teste unitário. Você copia a estrutura existente e ajusta o que precisa ser ajustado, em vez de começar do zero. O código completo está no repositório GitHub do canal, com o link na descrição do vídeo.

Dentro dessa pasta, crie um arquivo chamado creator.ts. Ele guarda o objeto que o usuário envia no endpoint: apenas os dois pontos, origem (start) e destino (destination). Nada de latitude, longitude ou distância vem do cliente, porque a API vai gerar tudo.

Depois, a aula cria a factory da classe MapsAdapter, com um arquivo MapsFactory e um index.ts para exportar essa classe. A factory segue o padrão Factory clássico: o método makeMapsAdapter não recebe nenhuma dependência complicada, porque a única coisa que ele faz é instanciar o GoogleMapsClient da biblioteca e devolver um new MapsAdapter(...) com esse client dentro.

Só depois disso o caso de uso consome a factory, o que deixa a troca de biblioteca de mapas viável no futuro, sem reescrever a regra de negócio.

Como a lógica de criação de rota funciona

No caso de uso, você começa limpando as dependências que não estão sendo usadas e troca as referências ao repositório antigo pelas corretas. O caso de uso importa o CreateRouteDTO (no lugar de um tipo genérico de map route) e recebe, como dependência, apenas o protocolo de directions, exposto pela API directions.

Aqui aparece uma decisão de design importante: as interfaces ficam separadas em dois métodos, e o caso de uso enxerga só o método getDirections. Ou seja, a classe depende do mínimo que ela realmente conhece, não do adapter inteiro. Se outro trecho do sistema precisa de outra função da biblioteca, ele injeta outra interface.

O fluxo dentro do caso de uso é este:

  1. Receber o data vindo do controller, com start e destination desestruturados.
  2. Chamar await directions.getDirections(start, destination) com esses dois pontos.
  3. Pegar o primeiro candidato (routes[0]) da resposta e a primeira posição do array legs.
  4. Extrair o endereço inicial, o endereço final e a distância dessa primeira perna da rota.
  5. Montar o objeto final, chamado mapRoute, com os campos que vão para o banco.

O objeto salvo é montado com spread dos dados recebidos (...data) e depois formatado. O name vem do DTO, os endereços vêm de start_address e end_address, e latitude e longitude saem de start_location e end_location. No meio do caminho existe um duration: 0 proposital, só para o tutorial andar mais rápido; a ideia é corrigir isso depois para pegar a duração direta da API.

No fim, o caso de uso repassa o objeto formatado para o repositório, que grava tudo em JSON (ou em string, dependendo do campo) no MongoDB. Campos como routes e request em formato string são úteis para o front-end consumir depois.

O que vai para o banco e o que fica de fora

A resposta bruta da API do Google Maps é grande. Guardar tudo seria ruim, então o adapter cria um objeto customizado e leva para o banco só o que interessa.

CampoOrigem na resposta da APIUso no banco
nameDTO enviado pelo usuárioIdentifica a rota
startAddressstart_address da primeira legExibição no front-end
endAddressend_address da primeira legExibição no front-end
startLocationstart_location (latitude e longitude)Chave geográfica de origem
endLocationend_location (latitude e longitude)Chave geográfica de destino
distancedistance da primeira legCálculo de custo e tempo
durationduração da rotaTempo estimado da viagem
routes / requestJSON bruto da chamadaReprocessamento e auditoria

Com a rota salva, o motorista ou entregador de aplicativo só precisa buscar a corrida pelo ID e usar esse ID como chave estrangeira. Esse consumo é assunto de uma aula posterior.

Testes unitários do fluxo de rota

Os testes unitários existem para garantir que a implementação funcione antes de qualquer coisa chegar ao banco. O ambiente de teste simula as chamadas à API, sem bater no Google Maps de verdade.

Para isso, configuramos mocks com uma proxy que devolve um fakeDirectionsData, no lugar da resposta real. O retorno do getDirections no teste é esse objeto falso com resolve, o que evita que os testes quebrem por dependência externa. Esses mocks também são reaproveitados mais adiante, e depois podem ser movidos para um arquivo separado.

Três correções aparecem no meio da aula e valem para quem estiver seguindo o passo a passo:

  • Um directions válido faltava nos imports, o que gerava erro de referência.
  • Uma chave e um parêntese ficaram abertos na instanciação, e o teste reclamou disso.
  • O teste ocasionalmente demorava muito para rodar, e um setTimeout com valor 7 foi adicionado para reduzir essa espera.

A metodologia de escrita também muda em relação à aula anterior. Antes, o código de produção veio primeiro e o teste depois. Aqui, o teste é escrito antes. Tanto faz a ordem, você escolhe; o importante é rodar yarn test até tudo passar.

Validação com schema e build do projeto

Um recurso interessante mostrado na aula é o Fastify, que permite definir o objeto de entrada e o objeto de saída com o schema personalizado. Tudo que não estiver no schema é cortado. Se o usuário não enviar start e destination, a API devolve um erro direto para o front-end, sem chegar ao banco.

Na prática, o schema é um pouco parecido com Swagger. A aula copia o mesmo esquema para os outros endpoints de load, load by page e assim por diante, porque a estrutura do objeto não muda muito e não há dado sensível ali para ocultar. O Fastify tem até um plugin que trabalha nesse formato, mas a exploração fica para outro momento.

Com o schema pronto, roda-se yarn build para verificar se o TypeScript do projeto inteiro continua compilando. Como o projeto é todo em TypeScript, um tipo errado derruba o build antes de chegar ao teste.

Teste de integração com rotas reais

Depois dos testes unitários, vem o teste de integração no map-route.test.ts. Nele, você passa start e destination de verdade, com dois lugares na Austrália, e roda yarn start para consumir os endpoints pelo Insomnia.

O resultado da aula é direto: a rota é adicionada, o request volta com o que era esperado, o name é gravado e a rota fica salva no banco sem ajuste manual. Se o usuário mandar uma segunda rota com o mesmo name, o fluxo já sobrescreve o registro.

Erros comuns e como resolver

  • Esquecer de passar directions na instanciação do caso de uso: o teste quebra na hora.
  • Deixar dependências de repositório antigas no caso de uso: imports que não são usados atrapalham o build do TypeScript.
  • Não fechar chave ou parêntese no ponto onde o adapter é criado: o arquivo não compila e o erro aponta para a linha errada.
  • Cache entre execuções de teste: uma alteração não aparece porque o cache antigo é reaproveitado.
  • Rodar o teste sem aguardar o mock: o fake não é encontrado e o teste falha sem motivo aparente.

Próximos passos do projeto

A aula termina com a implementação e os testes funcionando, e com um convite para acompanhar os próximos vídeos. O plano é discutir novos recursos em cima dessa base, incluindo o gerenciamento das rotas já criadas e o cache dessas rotas, que deve entrar no vídeo seguinte.

Se você quer entender melhor a lógica dos casos de uso apresentada aqui, o curso Crazystack Typescript explica essa estrutura do começo, incluindo a organização em camadas e a injeção de dependências. O canal também recomenda o Bootcamp do Dev Doido para quem quer seguir com projetos completos em Node.js e TypeScript.

Perguntas frequentes

  • Qual biblioteca o tutorial usa para falar com o Google Maps?

O adapter instancia o GoogleMapsClient da biblioteca oficial, dentro da MapsFactory. Assim o caso de uso depende só da interface de directions, não da lib diretamente, o que permite trocar a implementação depois sem reescrever a regra de negócio.

  • Quais dados o usuário precisa enviar para criar uma rota?

Apenas dois campos: o ponto de partida e o ponto de chegada. O objeto de entrada creator.ts tem só start e destination; o restante, como endereços, coordenadas e distância, é gerado pela API do Google Maps e formatado antes de ir para o banco.

  • Por que o campo duration aparece como zero no código?

Porque foi um preenchimento provisório para o tutorial não travar durante a gravação. A correção prevista é ler a duração direto da resposta da API, no array de legs da primeira rota, e salvar o valor real.

  • É obrigatório usar o Fastify para validar a entrada?

Não é obrigatório, mas o tutorial usa porque o schema personalizado corta qualquer campo fora do formato esperado e devolve erro direto ao front-end. Sem a validação, um payload incompleto só quebraria mais tarde, dentro do caso de uso ou na gravação.

  • Preciso bater na API real do Google Maps para rodar os testes?

Não. Os testes unitários usam mocks com uma proxy que retorna um fakeDirectionsData, então a suíte roda offline e continua estável mesmo se a API mudar. O teste de integração é que usa endereços reais e consome o endpoint pelo Insomnia.

  • Como o sistema evita chamadas repetidas à API?

Ainda não evita. O cache das rotas é tratado como o próximo passo do projeto, no vídeo seguinte, e é uma das razões para guardar o JSON bruto da resposta no banco junto com o resultado formatado.

  • Onde o projeto guarda as rotas criadas?

No MongoDB, em formato JSON, com os campos name, endereços, latitude e longitude de origem e destino, distância e duração. O motorista ou entregador busca depois pelo ID da corrida, usando esse ID como chave estrangeira.

  • Qual é a ordem de desenvolvimento dos testes?

Nesta aula o teste vem antes do código de produção, ao contrário da aula anterior. As duas abordagens funcionam; a diferença é que escrever o teste primeiro ajuda a definir o contrato do caso de uso antes de implementar.

  • O que acontece se eu não fechar uma chave no código?

O arquivo não compila e o teste falha com erro de sintaxe. Esse foi um dos tropeços da aula, resolvido fechando a chave e o parêntese na instanciação do MapsAdapter e rodando yarn test de novo.

  • O que preciso instalar para acompanhar o tutorial?

Node.js, TypeScript, o MongoDB rodando local e o Insomnia para testar os endpoints. O restante das dependências vem do projeto da aula anterior, disponível no repositório GitHub do canal.

Sobre o canal e o curso

A aula 87 faz parte do conteúdo do canal Gustavo Dev Doido, que mistura sotaque caipira, bordões como o do Serjão Berranteiro e ensina Node.js com TypeScript na prática. O mesmo professor mantém o curso Crazystack Typescript, onde a lógica de casos de uso e factories é explicada desde o início, e o Bootcamp do Dev Doido, voltado para quem quer montar projetos completos com backend em Node e front-end em Next.js.

Se você tem uma aula gravada, uma entrevista ou uma explicação técnica rolando solta em um vídeo no YouTube, o mesmo caminho se aplica: pasta do projeto, arquivo novo, adaptador, caso de uso e teste cobrindo o fluxo. A diferença é que o material de origem já existe, só falta virar texto.

Skala Blog

YouTube thumbnail: Como salvar rotas do Google Maps no Node.js com MongoDB

Source video

Como salvar rotas do Google Maps no Node.js com MongoDB

Create from this video

This article was created automatically from a new video.

Automate my channel

Turn your YouTube videos into articles

Built for creators with a backlog and no editor on staff. This page started as a video — Skalablog drafts from the transcript, you review every word, then publish on a first-party /p/{slug} page. Nothing goes live until you publish it.

Turn your videos into articles

Published with Skalablog