Skip to main content

SDK Publisher

Available in: Professional, Business, Enterprise tiers

Auto-generate a typed TypeScript API client from your Prisma schema, plus a matching Python client — CRUD methods and model interfaces for every model, with no hand-written fetch calls.

Why Use SDK Publisher

Problem: Building API clients is tedious and error-prone:

  • Manually writing fetch calls for every endpoint
  • Duplicating types between backend and frontend
  • Type safety lost across the network boundary
  • SDK maintenance when the schema changes
  • Keeping non-TypeScript consumers in sync

Solution: Auto-generate the client source for every model straight from the schema, so the method list and the model interfaces regenerate whenever your data model moves.

Benefits

  • Type Safety: Model interfaces generated from your Prisma schema
  • Auto Methods: Generated methods for all CRUD operations
  • Multi-Platform: TypeScript and Python clients from one schema
  • Small Surface: A thin fetch wrapper you can read end to end — no runtime SDK dependencies
  • Built-in Auth: Bearer token support on every request

Prerequisites

# Core dependencies for SDK generation
pnpm add @prisma/client zod

# PZG Pro license required

Generate

Add to your schema.prisma:

generator pzgPro {
provider = "node ./node_modules/prisma-zod-generator/lib/cli/pzg-pro.js"
output = "./generated/pro"
enableSDK = true
}

Then run:

prisma generate

Generated Files

generated/
pro/
sdk/
typescript/
index.ts # TypeScript SDK with APIClient class
python/
api_client.py # Python SDK

Both platforms are generated by default. Narrow the list with the platforms option:

generator pzgPro {
provider = "node ./node_modules/prisma-zod-generator/lib/cli/pzg-pro.js"
output = "./generated/pro"
enableSDK = true

// Optional advanced config (stringified JSON)
// sdk = "{ \"platforms\": [\"typescript\"] }"
}

The generator creates SDK clients with methods per resource:

  • Methods for all models (listUsers, createUser, getUser, etc.)
  • A TypeScript interface per model, plus enum declarations for your Prisma enums
  • Built-in bearer-token authentication

Basic Usage

The APIClient constructor takes positional arguments — the base URL, and optionally an API key that is sent as Authorization: Bearer <apiKey> on every request:

import { APIClient } from '@/generated/pro/sdk/typescript'

const client = new APIClient('http://127.0.0.1:3001', process.env.API_KEY)

// Methods resolve with the parsed JSON body and throw on non-2xx responses
try {
const users = await client.listUsers()
console.log(users)
} catch (err) {
console.error(err) // Error: HTTP 500
}

CRUD Operations

// Create
const user = await client.createUser({
email: 'user@example.com',
name: 'John Doe'
})

// Read all
const users = await client.listUsers()

// Read single
const one = await client.getUser('123')

// Update
const updated = await client.updateUser('123', { name: 'Jane Doe' })

// Delete
await client.deleteUser('123')

Error Handling

There is no result envelope: a method either resolves with the response body or throws. A non-2xx status throws Error: HTTP <status>, and network failures propagate the underlying fetch error.

try {
const user = await client.createUser(userData)
console.log('Created user:', user)
} catch (err) {
// Non-2xx response, or the request never completed
console.error(err instanceof Error ? err.message : err)
}
No client-side validation

The generated client does not validate requests or responses with Zod — validate with your core generated schemas before calling it, and treat the resolved body as untyped JSON where you need a guarantee.

Retry & Timeout Behavior

The generated client is a thin fetch wrapper: no retries, timeouts, or backoff are built in. Add them at the call site — for example by wrapping calls in your own retry helper, or by passing an AbortSignal.timeout(...)-driven fetch through your runtime.

Routes

The generated request paths are unprefixed and lowercase-pluralized from the model name, matching the API Docs mock server:

  • /users - User endpoints
  • /posts - Post endpoints

Point baseUrl at a prefix (for example https://api.example.com/api) if your real API is mounted under one.

Using the generated SDK

From 2.6.0+ the TypeScript output is a package: index.ts, a package.json built from packageName/version (plus publishConfig.registry when publishRegistry is set), and a README.md unless includeDocumentation: false. The Python output remains a single api_client.py.

The generator never publishes for you — that would fire on every prisma generate. The emitted package.json carries a publish script for CI to run.

Authentication

authConfig selects how the credential is attached:

ConfigHeader sent
omitted, or { "type": "bearer" }Authorization: Bearer <token>
{ "type": "bearer", "tokenPrefix": "Token" }Authorization: Token <token>
{ "type": "apikey", "headerName": "X-Api-Key" }X-Api-Key: <token>
{ "type": "oauth2" }Authorization: Bearer <token> — obtaining the token is your app's concern

headerName overrides the header for any type, and both the TypeScript and Python clients honour it (the Python client only from 2.7.0+ — before that authConfig was TypeScript-only). Before 2.6.0 the option was ignored entirely and every client sent a bearer Authorization header.

The Python output also carries a pyproject.toml and README.md from 2.7.0+, so it is installable the way the TypeScript package is. platforms accepts typescript and python; the type previously listed five more languages that only produced a runtime warning.

Types in the emitted client

Scalars are typed as JSON carries them, not as Prisma models them: Decimal and BigInt are string | number, Json is unknown, and Bytes and DateTime are string. Enums carry string values, so member.role === Role.ADMIN compares correctly against a response body.

Requires 2.4.1+

Earlier versions named Prisma's Decimal, JsonValue and Buffer in a client that has no Prisma dependency, so anything with one of those columns failed to compile (TS2304); enums were emitted without values, making them numeric, so those comparisons were silently always false; and a multi-line /// field comment broke the file outright.

See Also