InfraNode.dev

InfraNode with Java: HttpClient and OpenAPI Generator

Tutorial: fetch open data for German cities from Java, with the built-in java.net.http.HttpClient and Jackson or with a typed client that OpenAPI Generator builds from the official spec. No API key.

Option 1: HttpClient and Jackson

Since Java 11 the JDK ships java.net.http.HttpClient, which covers HTTP. For the JSON, Jackson is the only dependency you need (Maven):

<dependency>
  <groupId>com.fasterxml.jackson.core</groupId>
  <artifactId>jackson-databind</artifactId>
  <version>2.18.2</version>
</dependency>

Every response has the same envelope: the data under data, the source status under meta. For a single record the values sit in data.payload and the source credit in data.attribution.

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.time.Duration;

public class Main {
    private static final String BASE = "https://infranode.dev/api/v1";
    private static final HttpClient HTTP = HttpClient.newBuilder()
            .connectTimeout(Duration.ofSeconds(10))
            .build();
    private static final ObjectMapper JSON = new ObjectMapper();

    static JsonNode get(String path) throws Exception {
        HttpRequest req = HttpRequest.newBuilder(URI.create(BASE + path))
                .header("Accept", "application/json")
                .timeout(Duration.ofSeconds(30))
                .build();
        HttpResponse<String> res = HTTP.send(req, HttpResponse.BodyHandlers.ofString());
        if (res.statusCode() != 200) {
            throw new IllegalStateException("HTTP " + res.statusCode() + ": " + res.body());
        }
        return JSON.readTree(res.body());
    }

    public static void main(String[] args) throws Exception {
        JsonNode data = get("/cities/berlin/weather").path("data");
        System.out.println(data.path("payload").path("temperature_c").asDouble() + " °C");
        System.out.println("Source: " + data.path("attribution").path("text").asText());

        JsonNode cities = get("/cities").path("data");
        System.out.println(cities.size() + " cities");
    }
}

Every record carries its own attribution (source and license URL). The open data licenses require the credit, so show it to your users. The available data types are listed in the data catalog.

Option 2: typed client via OpenAPI Generator

If you prefer classes over JsonNode, generate the client from the official spec at https://infranode.dev/openapi.json. With Docker there is nothing to install locally:

docker run --rm -v "${PWD}:/local" openapitools/openapi-generator-cli:v7.10.0 generate \
  -i https://infranode.dev/openapi.json \
  -g java --library native \
  -o /local/infranode-client \
  --api-package dev.infranode.api \
  --model-package dev.infranode.model \
  --invoker-package dev.infranode.client

--library native builds on the JDK HttpClient and only pulls in Jackson. Then install the client into your local Maven repository:

cd infranode-client
mvn install -DskipTests

Add the generated client through the coordinates in its pom.xml (default: org.openapitools:openapi-java-client). Every data type has its own method, e.g. getCityWeather, getCityParking or getCityCharging:

import dev.infranode.api.CitiesApi;
import dev.infranode.client.ApiClient;
import dev.infranode.client.ApiException;
import dev.infranode.model.CanonicalRecord;
import dev.infranode.model.WeatherPayload;

public class Weather {
    public static void main(String[] args) throws ApiException {
        ApiClient client = new ApiClient();
        client.updateBaseUri("https://infranode.dev");
        CitiesApi cities = new CitiesApi(client);

        CanonicalRecord rec = cities.getCityWeather("berlin").getData();
        WeatherPayload w = rec.getPayload().getWeatherPayload();
        System.out.println(w.getTemperatureC() + " °C");
        System.out.println("Source: " + rec.getAttribution().getText());

        // Errors (404 unknown city, 503 source down) arrive as ApiException
        try {
            cities.getCityWeather("essen");
        } catch (ApiException e) {
            System.out.println("HTTP " + e.getCode());
        }
    }
}

payload is a union over all payload types, told apart by the kind field. Get the matching type with its getter (getWeatherPayload(), getParkingPayload(), ...) or generically with getActualInstance().

Further reading

Endpoints and parameters: API reference. List endpoints support optional paging, see pagination. For AI agents there is the InfraNode MCP server.

Frequently asked questions

Is there an official Java SDK?
No, there is no dedicated Java package. The API is lean enough for the built-in HttpClient, and for typed classes OpenAPI Generator builds a complete client from https://infranode.dev/openapi.json.
Do I need an API key?
No. The InfraNode API is free and keyless. No key, no sign-up.
Which Java version do I need?
Java 11 or newer. java.net.http.HttpClient has been part of the JDK since Java 11, and the generated client targets Java 11 as well.
Does this work with Kotlin or Spring?
Yes. The generated Java client runs unchanged in Kotlin. OpenAPI Generator also has dedicated targets such as -g kotlin, or --library resttemplate and webclient for Spring.