Skip to main content
Examples

OpenAPI & client generation

Fetch a generated API's OpenAPI document (/openapi.json or /openapi-enhanced.json) and generate typed clients with openapi-typescript, openapi-generator and NSwag.

Every generated API serves an OpenAPI 3.0 document built from your live schema and endpoint configuration:

URL Contents
/openapi.json Every route the API serves, with a stable operationId (listArtist, getArtist, createArtist, updateArtist, patchArtist, deleteArtist, …), its parameters (limit, offset, sort, fields, count and one filter per column), request and response schemas, every error status with the {"error": "..."} body, and its authentication
/openapi-enhanced.json The same, plus your documentation settings: title, description, contact, example values and authored examples

Both work for code generation; the operationIds are the same in both. Disabled endpoints are not in the documents.

Send your API key when fetching the document. Once your API's documentation is published, the documents are public; until then they need an X-API-Key the API itself would accept: a key for this API that is not revoked or expired, with API keys enabled for your organization and the key's IP allowlist (if any) satisfied. Without a key the answer is 401 {"error":"This documentation is private. Provide an X-API-Key header."}; with any other key, 401 {"error":"This documentation is private. The X-API-Key is not valid for this API."}.

curl -s "$RESTWARD_API_URL/openapi-enhanced.json" \
  -H "X-API-Key: $RESTWARD_API_KEY" -o openapi.json
const spec = await (await fetch(`${process.env.RESTWARD_API_URL}/openapi-enhanced.json`, {
  headers: { "X-API-Key": process.env.RESTWARD_API_KEY },
})).json();
spec = session.get(f"{api}/openapi-enhanced.json").json()
var spec = await http.GetStringAsync("/openapi-enhanced.json");
await File.WriteAllTextAsync("openapi.json", spec);

The commands below were run against the document of the Chinook sample API. The generated C# client was compiled.

TypeScript: openapi-typescript

Generates types only; pair it with openapi-fetch for a typed fetch wrapper.

Shell
npx openapi-typescript openapi.json -o src/chinook.d.ts
npm install openapi-fetch
TypeScript
import createClient from "openapi-fetch";
import type { paths } from "./chinook";

const client = createClient<paths>({
  baseUrl: process.env.RESTWARD_API_URL,
  headers: { "X-API-Key": process.env.RESTWARD_API_KEY },
});

const { data, error } = await client.GET("/api/public/artist", { params: { query: { limit: 3, name: "AC/DC" } } });

Python and other languages: openapi-generator

openapi-generator supports dozens of languages. With Docker:

Shell
docker run --rm -v "$PWD:/local" openapitools/openapi-generator-cli generate \
  -i /local/openapi.json -g python -o /local/chinook-client --package-name chinook_client
pip install ./chinook-client
Python
import os
import chinook_client

config = chinook_client.Configuration(host=os.environ["RESTWARD_API_URL"])
config.api_key["ApiKeyAuth"] = os.environ["RESTWARD_API_KEY"]

with chinook_client.ApiClient(config) as client:
    api = chinook_client.DefaultApi(client)
    artists = api.list_artist(limit=3)

Swap -g python for typescript-fetch, java, go, kotlin, … as needed.

C#: NSwag

Shell
dotnet tool install -g NSwag.ConsoleCore
nswag openapi2csclient /input:openapi.json /output:ChinookClient.cs /namespace:Chinook \
  /classname:ChinookClient /jsonLibrary:SystemTextJson /generateOptionalParameters:true

NSwag's default settings produce a client that compiles. /generateOptionalParameters:true makes the query parameters optional, so you pass only the ones you need.

C#
var http = new HttpClient();
http.DefaultRequestHeaders.Add("X-API-Key", Environment.GetEnvironmentVariable("RESTWARD_API_KEY"));

var client = new Chinook.ChinookClient(Environment.GetEnvironmentVariable("RESTWARD_API_URL"), http);
var artists = await client.ListArtistAsync(limit: 3);

Things to know about the generated clients

  • The base URL is the platform address. The first servers entry is the address RestWard assigned to your API (the one on your dashboard and in restward list); generated clients use it by default. Your documentation custom domain is listed after it, never first, once one of your organization's verified domains covers it: verification proves you own the domain, not that it routes to your API. Neither ever comes from the request or from text in your settings. When the platform has no address for the API, the document has no servers: pass the base URL to the client, as in the examples above.
  • Equality filters are typed parameters; operators are not. List methods take limit, offset, sort, fields, count and one parameter per column (name=AC/DC). The operator form column[op]=value (filters) cannot be described as a typed parameter: add those query parameters yourself or call the list route with a plain HTTP client.
  • count=true changes the response shape. The list response is documented as an array of rows; with count=true the body is {"total": n, "items": [...]} (the <table>_page schema) and typed list methods cannot read it. Call the route directly when you need the total, or read the X-Total-Count header.
  • fields returns partial rows. No row property is required, so models deserialize a projection; the columns you did not ask for are unset.
  • Create returns the new key. The 201 response is the <table>_created schema, {"id": <new key>}, typed like the first primary-key column. id is nullable where the API cannot return the key: tables without a primary key, and MySQL tables whose key is not AUTO_INCREMENT (MySQL cannot report a key that a column default or a trigger filled in, so id is null when the request omits it).
  • Generated keys are readOnly. Identity / auto-increment keys and generated columns are marked readOnly and are not in the create and update schemas; the database fills them in.
  • Prefer PATCH for partial updates. PATCH (patch<Table>) updates exactly the columns in the body. PUT (update<Table>) also writes every column in the body, so with a generator that serializes unset properties as null, pass a complete row.
  • Errors are typed. Every operation lists the statuses it can return (400, 401, 403, 404, 409, 413, 422, 429, 500) with the shared Error schema ({"error": "...", "constraint"?, "column"?, "parameter"?}); 429 carries Retry-After. See Errors & rate limits.
  • Authentication follows the endpoint configuration. Each operation lists the scheme it accepts: ApiKeyAuth (X-API-Key), BearerAuth (JWT) or none for public endpoints. OpenAPI 3.0 cannot describe client certificates, so mTLS endpoints are marked x-restward-auth: mtls; configure the certificate on your HTTP client. Configure one credential per client: a request is checked with the first it carries (API key, then bearer token, then certificate), so a stale API key next to a valid token is a 401.

Reconnecting to server...

Please wait while we restore your connection

Connection lost

Retrying in seconds...

Unable to reconnect

The server connection has been lost.
You may need to log in again.

Session Paused

The server has temporarily paused your session.

Session Expired

Your session could not be resumed.
Please reload the page to continue.