Claude Fable 5.1 & GPT-6 Astra packages are live

Sdk

Free

Publishing client SDKs that stay correct — generation from the spec, retries and idempotency, typed errors, versioning, and release automation.

194 lines7.9 KB Glm API
targetModels
GLM-5.3GLM-5.2GLM-5 FamilyGLM-4.6Future GLM Models
name
sdk
category
API
description
Publishing client SDKs that stay correct — generation from the spec, retries and idempotency, typed errors, versioning, and release automation.
license
MIT
author
Agent.md maintainers
last-verified
reviewed-by
unreviewed
<!-- Generated from models/_canonical by scripts/build-model-variants.js. Edit the canonical source, not this file. Behavioural profile for GLM: scripts/model-profiles.json -->

#Task boundary

  1. Implement only what the task names; no extra abstractions or files.
  2. English-only comments and identifiers.
  3. Stop when the checklist passes.

#Purpose

Rules for shipping client libraries for your API. An SDK is a second public contract with a second deprecation cycle, so the only sustainable approach is to generate it from the specification and hand-write as little as possible.

An SDK's job is to remove work the caller would otherwise repeat badly: auth, retries, pagination, error typing, and idempotency. It is not a place for business logic. → API/open-api


#Generate, do not maintain

bash
# Types only — smallest surface, no runtime dependency
npx openapi-typescript spec/openapi.json -o src/schema.d.ts

# Full typed client
npx orval --config orval.config.ts
LanguageGenerator
TypeScriptopenapi-typescript, orval, @hey-api/openapi-ts
Pythonopenapi-python-client, datamodel-code-generator
Gooapi-codegen
Java / Kotlinopenapi-generator
Rustprogenitor
Multi-language, commercialStainless, Speakeasy, Fern

The hand-written layer is a thin wrapper: transport, auth, retries, pagination helpers, error classes. Everything shaped by the API is generated.

  1. Regenerate in CI on every spec change, and fail if the committed output differs.
  2. Never hand-edit generated files. The next regeneration discards the edit; fix the spec or the generator template.
  3. Method names come from operationId, so renaming one is a breaking SDK change. → API/versioning

#What the SDK must handle

ts
const client = new Acme({
  apiKey: process.env.ACME_API_KEY,   // never a hard-coded default
  baseUrl: process.env.ACME_BASE_URL ?? "https://api.acme.com",
  timeout: 30_000,
  maxRetries: 3,
});
ConcernBehaviour
Retries429, 5xx, connection errors and timeouts only. Exponential backoff with jitter, honouring Retry-After
IdempotencyGenerate and attach an Idempotency-Key on every retryable write, stable across retries of the same call
TimeoutsA default request timeout, overridable per call
PaginationAn async iterator, so callers never hand-roll cursor loops
ErrorsTyped classes carrying status, machine code and requestId
AuthFrom the environment by default; never logged, never in a URL
User agentacme-node/2.3.1 node/22.4 — makes support and deprecation outreach possible
ts
// The auto-generated idempotency key must not change between retries, or a
// timeout-then-retry creates two charges.
for await (const order of client.orders.list({ status: "paid" })) { … }

Never retry a non-idempotent request without an idempotency key. The default retry policy plus a POST /payments is how a customer is charged twice.

Never retry 4xx other than 429 and 408 — the request is wrong, not late.


#Errors

ts
try {
  await client.payments.create({ amountCents: 5000, currency: "EUR" });
} catch (e) {
  if (e instanceof InsufficientFundsError) { … }        // typed, branchable
  if (e instanceof RateLimitError) { await sleep(e.retryAfterMs); }
  if (e instanceof AcmeApiError) console.error(e.requestId, e.code, e.status);
}
  1. One base error class, with subclasses per category (auth, validation, rate limit, server, network).
  2. Always expose status, code and requestId. The requestId is what makes a support ticket resolvable.
  3. Never swallow an error into a null return. The caller cannot distinguish "absent" from "failed".
  4. Never include the API key in an error message or a serialised request dump.

#Versioning and release

  1. SemVer, judged from the SDK consumer's perspective: a new optional API field is a minor bump; a renamed method is a major one even if the API call is unchanged.
  2. Record the API version the SDK targets, and send it as a header.
  3. Publish a changelog with every release, generated from Conventional Commits.
  4. Automate publishing (semantic-release, changesets) — a manual release process produces skipped versions and unpublished fixes.
  5. Support the runtime versions your users actually run, declare them in engines/python_requires, and test the oldest in CI.
  6. Ship provenance/attestation (npm publish --provenance) so consumers can verify the artefact came from your repository.

#Packaging and ergonomics

  1. Zero or near-zero runtime dependencies. Every dependency is a supply-chain surface and a version conflict for the consumer.
  2. Ship ESM and CJS with correct exports conditions; ship type definitions.
  3. Support cancellation (AbortSignal, context) on every call.
  4. Allow injecting a custom fetch/transport for proxies and instrumentation.
  5. Make the first call work in under five minutes: install, set one environment variable, copy one runnable example from the README.
  6. Ship a runnable example per major workflow, tested in CI so it cannot rot. → Documentation/api-docs

#Anti-patterns

Anti-patternWhy it failsFix
Hand-written client for a large APIDrifts from the spec immediatelyGenerate from OpenAPI
Hand-edited generated filesLost on regenerationFix the spec or template
Generated output not committedNo diff, no reviewCommit and check freshness in CI
Retrying non-idempotent writesDuplicate chargesIdempotency key, stable across retries
Retrying all 4xxHammering a permanently invalid request429/408 only
Fixed-interval retriesSynchronised thundering herdBackoff with jitter
Ignoring Retry-AfterFights the server's own guidanceHonour it
No request timeoutHangs forever on a stalled connectionDefault timeout
Untyped errorsCallers parse message stringsError class hierarchy
No requestId on errorsSupport tickets unresolvableExpose it
Manual cursor loops left to callersEveryone implements it differently, some wronglyAsync iterator
API key in the user agent or URLLeaks into logsAuthorization header only
Heavy dependency treeSupply-chain surface and version conflictsNear-zero dependencies
Manual publishingSkipped versions, unpublished fixesAutomated release
No deprecation signallingUsers discover removal at runtimeWarn on deprecated methods

#Checklist

  • The API-shaped surface is generated from the OpenAPI/proto specification
  • Generated output is committed and CI fails when it is stale
  • No generated file is hand-edited
  • Retries cover only 429, 408, 5xx, timeouts and connection errors
  • Backoff is exponential with jitter and honours Retry-After
  • Retryable writes carry an idempotency key that is stable across retries
  • A default request timeout exists and is overridable per call
  • Pagination is exposed as an async iterator
  • Errors are typed and expose status, code and requestId
  • Credentials are read from the environment, never logged or placed in URLs
  • A descriptive user agent identifies SDK and runtime versions
  • Cancellation is supported on every call
  • Runtime dependencies are minimal and declared support ranges are tested
  • SemVer is applied from the consumer's perspective, with a published changelog
  • Releases are automated, with provenance attestation
  • A runnable quickstart example is tested in CI