Pular para o conteúdo

Integrações

SDK para TypeScript

Use a API em TypeScript ou JavaScript: um método tipado para cada chamada, e funções prontas para acompanhar uma coleta e validar um webhook.

Instale

O pacote é o gmaps-sdk, sem nenhuma dependência.

npm i gmaps-sdk

Crie um cliente

Passe sua chave, que aqui vem de uma variável de ambiente. Cada método faz uma chamada e devolve o que a API responde em data:

import { createClient } from "gmaps-sdk";

const gmaps = createClient({ apiKey: process.env.GMAPS_API_KEY });

const result = await gmaps.places.search({
  keywords: ["padaria"],
  geo: { location: "Curitiba, PR" }
});
console.log(result.places.map((place) => place.identity.name));

O que o createClient aceita:

OpçãoPadrãoPara que serve
apiKey-Sua chave. Sem ela, só o attributions.list() funciona.
baseUrlhttps://api.gmaps.dev/v1O endereço da API, terminando em /v1. Só mude se for chamar uma API local.
timeout30000Quanto tempo cada tentativa de uma chamada pode levar, em milissegundos, contando a leitura da resposta. Uma busca com wait sempre ganha a espera inteira, e um pouco mais.
maxRetries2Quantas vezes uma chamada que falhou é tentada de novo, quando isso é seguro. Com 0, o SDK nunca tenta de novo.
fetchglobalThis.fetchSeu próprio fetch, para testes ou para um ambiente que não tenha um.

Para dar outro limite de tempo a uma chamada, ou cancelá-la, passe { timeoutMs, signal } como último argumento.

const usage = await gmaps.usage.get({ timeoutMs: 5000 });

Se uma chamada fica sem resposta ou recebe um erro do nosso lado (um 5xx), o SDK manda de novo depois de cerca de 1 segundo, e mais uma vez cerca de 2 segundos depois. Isso só vale para chamadas que podem rodar duas vezes sem problema: leituras, estimativas, consultas de cobertura, cancelamentos e mudanças e exclusões de webhook. Uma busca, uma coleta nova, uma exportação nova, um webhook novo e um teste de webhook nunca são repetidos assim, porque a primeira tentativa pode já ter rodado, e uma segunda poderia gastar créditos, enviar algo duas vezes ou criar uma segunda exportação na sua lista. Depois de um 408, 425 ou 429 que peça uma espera de até 10 segundos, qualquer chamada espera e vai de novo.

Todos os métodos

Cada método recebe um objeto com os mesmos campos da chamada da API. Os que não têm campos, como usage.get(), recebem só as opções.

MétodoChamada da APIPara que serve
places.search(params)POST /v1/places/searchEncontra lugares pelo que são e onde ficam. Veja Busca.
places.get({ place })GET /v1/places/{place}Lê um lugar que já salvamos, pelo nosso id, pelo cid, pelo place ID ou por um link do Maps. Veja Lugares.
collections.quote(params)POST /v1/collections/quoteMostra quanto custaria coletar uma região, sem começar nada.
collections.create(params)POST /v1/collectionsColeta todos os lugares de uma região. Veja Coletas.
collections.get({ id })GET /v1/collections/{id}Mostra até onde a coleta chegou e quanto já custou.
collections.list(params?)GET /v1/collectionsAs suas coletas, das mais novas para as mais antigas.
collections.places({ id })GET /v1/collections/{id}/placesOs lugares que uma coleta encontrou, uma página por vez.
collections.cancel({ id })DELETE /v1/collections/{id}Para uma coleta. Os lugares que já chegaram continuam seus, e os créditos gastos com eles não voltam.
watchCollection(id, options?)GET /v1/collections/{id}/streamEntrega os lugares novos de uma coleta à medida que chegam. Veja abaixo.
exports.create(params)POST /v1/exportsGera um link com os lugares de uma coleta em csv, json ou xlsx. Veja Exportações.
exports.get({ id })GET /v1/exports/{id}Lê uma exportação de novo, enquanto o link ainda funciona.
coverage.get(params)POST /v1/coverageMostra quanto de uma região já buscamos e quantos lugares temos lá. Veja Cobertura.
usage.get()GET /v1/usageCréditos usados e restantes, no dia e no mês.
webhooks.create(params)POST /v1/webhooksAdiciona um endpoint. O segredo de assinatura dele aparece só nessa resposta. Veja Webhooks.
webhooks.list()GET /v1/webhooksOs seus endpoints, e se cada um está ligado.
webhooks.update({ id, … })POST /v1/webhooks/{id}Muda os eventos ou a anotação de um endpoint, ou desliga e liga.
webhooks.test({ id })POST /v1/webhooks/{id}/testManda um evento de teste para um endpoint.
webhooks.deliveries({ id })GET /v1/webhooks/{id}/deliveriesO que mandamos para um endpoint e o que ele respondeu.
webhooks.delete({ id })DELETE /v1/webhooks/{id}Remove um endpoint e o histórico de entregas dele.
attributions.list()GET /v1/attributionsOs projetos de código aberto que usamos. Não precisa de chave.

Acompanhe a chegada dos lugares novos

Quando os lugares que já salvamos não chegam ao limit, a resposta da busca traz uma collection, a coleta que está buscando o resto. O watchCollection entrega cada lugar novo assim que ele chega:

const result = await gmaps.places.search({
  keywords: ["padaria"],
  geo: { location: "Curitiba, PR" }
});

if (result.collection) {
  for await (const frame of gmaps.watchCollection(result.collection.id)) {
    if (frame.type === "place") console.log(frame.data);
  }
}
  • Cada evento tem um type e os dados dele em data. O place traz um lugar novo, o progress traz as contagens até ali e o done vem por último. Progresso ao vivo lista todos os tipos. Uma coleta grande para de mandar eventos place depois de um certo número, então leia os lugares dela com collections.places quando ela terminar.
  • O loop termina sozinho depois do done. Para parar antes, use break.
  • Se a conexão cair, ele conecta de novo e continua depois do último evento que entregou, então nenhum evento chega duas vezes nem se perde. Depois de quedas demais seguidas, sem nenhum evento entre elas, ele lança um GmapsError com NETWORK_ERROR.

O que o segundo argumento aceita:

OpçãoPadrãoPara que serve
lastEventId-Começa depois do evento com este id, para continuar de onde você parou.
signal-Um AbortSignal. Quando ele dispara, o loop termina sem erro.
retryMs2000Quantos milissegundos esperar antes de conectar de novo.
maxRetries10Quantas quedas seguidas ele aceita antes de desistir. Qualquer evento, até um heartbeat, zera a contagem.

Você não precisa acompanhar. Dá para fazer a mesma busca mais tarde, quando os lugares novos voltam de graça, ou pedir que a busca espere por eles com wait. Busca explica os dois jeitos.

Quando uma chamada falha

Toda chamada que falha lança um GmapsError. Ele traz o que a API disse, para o seu código saber qual foi o problema:

import { GmapsError } from "gmaps-sdk";

try {
  await gmaps.usage.get();
} catch (error) {
  if (!(error instanceof GmapsError)) throw error;
  console.error(error.status, error.code, error.message);
  console.error(`Request id: ${error.requestId}`);
}
CampoO que traz
codeO que deu errado, como QUOTA_EXCEEDED. Erros lista todos os códigos.
statusO status HTTP, ou 0 quando nenhuma resposta chegou.
messageO que aconteceu e o que fazer agora, em inglês.
requestIdO nosso id da chamada. Mande junto quando falar com a gente.
retryAfterQuantos segundos esperar antes de tentar de novo, quando a API informa.
detailsMais sobre o problema, como o scope num erro de cota.
messageKeyCom params: um nome fixo para a mensagem e os valores dela, para o seu app mostrar a mensagem no idioma dele.

Quatro códigos vêm do próprio SDK, quando não há uma resposta que ele consiga usar:

  • TIMEOUT: a API não respondeu dentro do timeout.
  • NETWORK_ERROR: não deu para chegar à API, ou a resposta foi cortada no meio.
  • INVALID_RESPONSE: o status veio como sucesso, mas o corpo não era uma resposta da API: veio sem data, com um error ou nem era JSON. Quase sempre falta o /v1 no baseUrl.
  • HTTP_ERROR: veio um erro fora do nosso formato, quase sempre de algo no caminho entre você e a API.

Se você cancelar uma chamada com o seu próprio signal, o SDK lança o reason dele no lugar do erro. Opções que não funcionam, como um baseUrl que não é http nem https, dão erro assim que você cria o cliente.

Valide um webhook

O verifyWebhook diz se uma requisição veio mesmo da gente. Passe o segredo de assinatura do endpoint, o corpo bruto e o cabeçalho x-gmaps-signature:

import { verifyWebhook } from "gmaps-sdk";

const body = await request.text();
const signed = await verifyWebhook(
  process.env.GMAPS_WEBHOOK_SECRET,
  body,
  request.headers.get("x-gmaps-signature"),
);
if (!signed) return new Response(null, { status: 400 });
const event = JSON.parse(body);

Ele devolve false para qualquer falha: sem cabeçalho, outro segredo, corpo alterado ou assinatura com mais de 5 minutos. Leia o corpo como texto e só faça o parse do JSON depois de validar: fazer o parse e serializar de novo muda os bytes. Webhooks explica o resto.

Tipos

Toda resposta é tipada, e cada tipo de resposta é exportado pelo nome para você usar no seu código:

import type { CollectionDto, PlaceDto, SearchResultDto } from "gmaps-sdk";

Os parâmetros de cada método também são exportados, como o SearchPlacesParams, para você tipar uma chamada antes de fazê-la:

import type { SearchPlacesParams } from "gmaps-sdk";

const ask: SearchPlacesParams = {
  keywords: ["padaria"],
  geo: { location: "Curitiba, PR" }
};

Onde roda

  • Node 22 ou mais novo, Bun, ou qualquer ambiente com fetch, AbortSignal.any e AbortSignal.timeout.
  • É um módulo ES e já vem com as declarações de tipo. Carregue com import.
  • Não tem dependências. As chamadas usam o fetch do próprio ambiente, e o verifyWebhook usa a Web Crypto.
  • Também roda no navegador, mas deixe sua chave num servidor. No navegador, qualquer pessoa consegue ler a chave.