SDK

APIは通常のHTTPSとJSONなので、すでにどの言語からでも使えます。加えて、以下の各言語向けの公式ライブラリを開発しています。どれも意図的に小さく作っています。8つのエンドポイント用の型付きクライアント、タイムアウト、429503での再試行、オートコンプリートのデバウンスです。フレームワークもプラグインもありません。

言語別の状況

JavaScriptとTypeScript 開発中

npm install @mygeocode/sdk

Node 18以降、Deno、Bun、ブラウザで動作します。型定義付きです。

Python 開発中

pip install mygeocode

Python 3.9以降。同期と非同期のクライアント、httpx以外の依存関係なし。

PHP 予定

composer require mygeocode/sdk

PHP 8.1以降。PSR-18クライアントで、HTTPライブラリはお好みのものを使えます。

Go 予定

go get github.com/mygeocode/mygeocode-go

標準ライブラリのみ。Contextに対応。

Java 予定

com.mygeocode:sdk

Java 11以降。java.net.httpをベースにしており、Kotlinからも使えます。

C# 予定

dotnet add package MyGeocode

.NET 6以降。全面的に非同期、System.Text.Jsonを使用。

Ruby 予定

gem install mygeocode

Ruby 3.0以降。Net::HTTPを使用し、実行時の依存関係なし。

Rust 予定

cargo add mygeocode

reqwestとserdeを使用し、既定で非同期。

上記のインストールコマンドは予約済みで、各ライブラリがリリースされた日から使えるようになります。リリース時期を知りたい場合は、support@mygeocode.comに空のメールを送ってください。インストールできるものができた時点で一度だけ返信します。

すべてのSDKの機能

エンドポイントごとに1つのメソッド

client.forward("...")client.reverse(lat, lon)client.ipv4("8.8.8.8")などです。戻り値はJSONをそのまま反映した型付きのレコードで、フィールド名も同じです。

ヘッダーに従った再試行

リセット時刻付きの429は、設定した上限の範囲内で、その時刻の後に再試行されます。503はバックオフを入れて再試行されます。それ以外はすべて、APIのcodemessageを含むエラーとして送出されます。

キーの扱い

キーはコンストラクターに渡すか、環境変数MYGEOCODE_KEYに設定してください。ディスクには何も書き込まれません。キーがない場合は、ダッシュボードへの案内とともにすぐにエラーになります。

今すぐHTTP APIを使う

最初のSDKが対応する言語で、エラー処理と429での再試行を含む逆ジオコーディングの例を示します。これは、ライブラリが内部で行う処理とほぼ同じです。

const BASE = "https://api.mygeocode.com/v1";

async function reverse(lat, lon, { key = process.env.MYGEOCODE_KEY, retries = 2 } = {}) {
  const url = new URL(BASE + "/reverse");
  url.searchParams.set("lat", lat);
  url.searchParams.set("lon", lon);

  const headers = key ? { "X-API-Key": key } : {};
  const res = await fetch(url, { headers });

  if (res.status === 429 && retries > 0) {
    const reset = Number(res.headers.get("X-Quota-Reset")) * 1000;
    const wait = Math.min(Math.max(reset - Date.now(), 1000), 60_000);
    await new Promise((r) => setTimeout(r, wait));
    return reverse(lat, lon, { key, retries: retries - 1 });
  }

  const data = await res.json();
  if (data.status !== "ok") throw new Error(`${data.error.code}: ${data.error.message}`);
  return data.result;
}

const place = await reverse(48.8584, 2.2945);
console.log(place.formatted, place.precision);
import os
import time
import requests

BASE = "https://api.mygeocode.com/v1"


class MyGeocodeError(Exception):
    pass


def reverse(lat, lon, key=os.environ.get("MYGEOCODE_KEY"), retries=2):
    headers = {"X-API-Key": key} if key else {}
    r = requests.get(f"{BASE}/reverse", params={"lat": lat, "lon": lon}, headers=headers, timeout=10)

    if r.status_code == 429 and retries > 0:
        reset = int(r.headers.get("X-Quota-Reset", 0))
        time.sleep(min(max(reset - time.time(), 1), 60))
        return reverse(lat, lon, key=key, retries=retries - 1)

    data = r.json()
    if data["status"] != "ok":
        raise MyGeocodeError(f'{data["error"]["code"]}: {data["error"]["message"]}')
    return data["result"]


place = reverse(48.8584, 2.2945)
print(place["formatted"], place["precision"])
<?php
const BASE = "https://api.mygeocode.com/v1";

function reverse(float $lat, float $lon, ?string $key = null, int $retries = 2): array
{
    $key ??= getenv("MYGEOCODE_KEY") ?: null;
    $ch = curl_init(BASE . "/reverse?" . http_build_query(["lat" => $lat, "lon" => $lon]));
    curl_setopt_array($ch, [
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_TIMEOUT => 10,
        CURLOPT_HTTPHEADER => $key ? ["X-API-Key: $key"] : [],
    ]);
    $body = curl_exec($ch);
    $status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
    curl_close($ch);

    if ($status === 429 && $retries > 0) {
        sleep(5);
        return reverse($lat, $lon, $key, $retries - 1);
    }

    $data = json_decode($body, true);
    if ($data["status"] !== "ok") {
        throw new RuntimeException($data["error"]["code"] . ": " . $data["error"]["message"]);
    }
    return $data["result"];
}

$place = reverse(48.8584, 2.2945);
echo $place["formatted"], " (", $place["precision"], ")\n";
package main

import (
	"encoding/json"
	"errors"
	"fmt"
	"net/http"
	"net/url"
	"os"
	"strconv"
	"time"
)

const base = "https://api.mygeocode.com/v1"

type Result struct {
	Formatted string  `json:"formatted"`
	Lat       float64 `json:"lat"`
	Lon       float64 `json:"lon"`
	Precision string  `json:"precision"`
}

type envelope struct {
	Status string  `json:"status"`
	Result *Result `json:"result"`
	Error  *struct {
		Code    string `json:"code"`
		Message string `json:"message"`
	} `json:"error"`
}

func reverse(lat, lon float64, retries int) (*Result, error) {
	q := url.Values{"lat": {strconv.FormatFloat(lat, 'f', -1, 64)}, "lon": {strconv.FormatFloat(lon, 'f', -1, 64)}}
	req, _ := http.NewRequest("GET", base+"/reverse?"+q.Encode(), nil)
	if key := os.Getenv("MYGEOCODE_KEY"); key != "" {
		req.Header.Set("X-API-Key", key)
	}
	resp, err := (&http.Client{Timeout: 10 * time.Second}).Do(req)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

	if resp.StatusCode == 429 && retries > 0 {
		time.Sleep(5 * time.Second)
		return reverse(lat, lon, retries-1)
	}

	var env envelope
	if err := json.NewDecoder(resp.Body).Decode(&env); err != nil {
		return nil, err
	}
	if env.Status != "ok" {
		return nil, errors.New(env.Error.Code + ": " + env.Error.Message)
	}
	return env.Result, nil
}

func main() {
	place, err := reverse(48.8584, 2.2945, 2)
	if err != nil {
		panic(err)
	}
	fmt.Println(place.Formatted, place.Precision)
}

他のプロバイダーのSDKをお使いですか?

当社のSDKは必要ないかもしれません。Google Maps、Bing、HERE、Mapbox、ipinfoの公式クライアントライブラリの多くはカスタムのベースURLを受け付け、当社の互換ホストはそれらのプロバイダーの形式で応答します。ライブラリの接続先を対応するwww.mygeocode.comのホストにすれば、今のコードをそのまま使えます。