npm package: @prisma/management-api-sdk
Description: TypeScript SDK for the Prisma Developer Platform Management API
Latest version: 1.24.0

# Management API SDK

A TypeScript SDK for the Prisma Developer Platform Management API. Use the simple client for direct API access, or the full SDK with built-in OAuth authentication and automatic token refresh.

Based on the [public OpenAPI 3.1 specification](https://api.prisma.io/v1/swagger-editor).

## Installation

```bash
npm install @prisma/management-api-sdk
```

## Basic Usage

For usage with an existing access or [service token](https://www.prisma.io/docs/postgres/introduction/management-api#service-tokens).

### Making API Calls

The client provides fully typed methods for all API endpoints:

```typescript
import { createManagementApiClient } from '@prisma/management-api-sdk'

const client = createManagementApiClient({
  token: 'your-access-token',
})

// List workspaces
const { data: workspaces, error } = await client.GET('/v1/workspaces')

// Get a specific project
const { data: project } = await client.GET('/v1/projects/{id}', {
  params: { path: { id: 'project-id' } },
})

// Create a new project
const { data: newProject } = await client.POST('/v1/workspaces/{workspaceId}/projects', {
  params: { path: { workspaceId: 'workspace-id' } },
  body: { name: 'My New Project' },
})

// Create a new database
const { data: newDatabase } = await client.POST('/v1/projects/{projectId}/databases', {
  params: { path: { projectId: 'project-id' } },
  body: {
    name: 'my-new-db-instance',
    region: 'us-east-1',
    isDefault: true,
  },
})

// Delete a database
const { error: deleteError } = await client.DELETE('/v1/databases/{databaseId}', {
  params: { path: { databaseId: 'database-id' } },
})
```

### Customizing the Client

You can override any `ClientOptions` from `openapi-fetch`, including `baseUrl`, `headers`, and other fetch options:

```typescript
import { createManagementApiClient } from '@prisma/management-api-sdk'

// Override baseUrl and add custom headers
const client = createManagementApiClient({
  token: 'your-access-token',
  baseUrl: 'https://api.example.com',
  headers: {
    'X-Custom-Header': 'value',
  },
})
```

**Note:** If you provide both `token` and `headers.Authorization`, the `headers.Authorization` takes precedence. The `baseUrl` defaults to `https://api.prisma.io` if not provided.

## Advanced Usage

For applications that need [OAuth authentication](https://www.prisma.io/docs/postgres/introduction/management-api#oauth-20-authentication), automatic token refresh, and token storage management, use the full SDK.

### OAuth Authentication Flow

The SDK uses OAuth 2.0 with PKCE for secure authentication. The flow is stateless - you're responsible for storing the `state` and `verifier` between the login URL generation and callback handling.

#### 1. Create the SDK Instance

```typescript
import { createManagementApiSdk, type TokenStorage } from '@prisma/management-api-sdk'

// Implement token storage for your environment
const tokenStorage: TokenStorage = {
  async getTokens() {
    const stored = localStorage.getItem('prisma-tokens')
    return stored ? JSON.parse(stored) : null
  },
  async setTokens(tokens) {
    localStorage.setItem('prisma-tokens', JSON.stringify(tokens))
  },
  async clearTokens() {
    localStorage.removeItem('prisma-tokens')
  },
}

// Create the SDK instance
const api = createManagementApiSdk({
  clientId: 'your-oauth-client-id',
  redirectUri: 'https://your-app.com/auth/callback',
  tokenStorage,
})
```

#### 2. Initiate Login

Generate the OAuth login URL. The returned `state` and `verifier` must be stored (e.g., in a session or cookie) for use when handling the callback:

```typescript
const { url, state, verifier } = await api.getLoginUrl({
  scope: 'workspace:admin offline_access',
  additionalParams: {
    utm_source: 'my-app',
    utm_medium: 'login',
  },
})

// Store state and verifier for the callback (e.g., in session storage)
sessionStorage.setItem('oauth-state', state)
sessionStorage.setItem('oauth-verifier', verifier)

// Redirect user to the login URL
window.location.href = url
```

#### 3. Handle the Callback

When the user is redirected back to your app, retrieve the stored `state` and `verifier` and pass them to `handleCallback`. On success, tokens are automatically stored via your `tokenStorage` implementation:

```typescript
// In your callback route handler
const callbackUrl = window.location.href

// Retrieve the stored values
const expectedState = sessionStorage.getItem('oauth-state')
const verifier = sessionStorage.getItem('oauth-verifier')

// Clean up stored values
sessionStorage.removeItem('oauth-state')
sessionStorage.removeItem('oauth-verifier')

try {
  await api.handleCallback({
    callbackUrl,
    verifier,
    expectedState,
  })
  // Tokens are now stored in tokenStorage and the client is ready to use
  console.log('Login successful!')
} catch (error) {
  if (error instanceof AuthError) {
    console.error('Authentication failed:', error.message)
  }
}
```

#### 4. Make API Calls

The client automatically includes authentication headers and refreshes tokens when they expire:

```typescript
// List workspaces
const { data: workspaces } = await api.client.GET('/v1/workspaces')

// Get a specific project
const { data: project } = await api.client.GET('/v1/projects/{id}', {
  params: { path: { id: 'project-id' } },
})

// Create a new project
const { data: newProject } = await api.client.POST('/v1/workspaces/{workspaceId}/projects', {
  params: { path: { workspaceId: 'workspace-id' } },
  body: { name: 'My Project' },
})

// Create a new database
const { data: newDatabase } = await api.client.POST('/v1/projects/{projectId}/databases', {
  params: { path: { projectId: 'project-id' } },
  body: {
    name: 'production',
    region: 'us-east-1',
    isDefault: true,
  },
})

// Delete a database
const { error: deleteError } = await api.client.DELETE('/v1/databases/{databaseId}', {
  params: { path: { databaseId: 'database-id' } },
})
```

#### 5. Logout

```typescript
await api.logout() // Clears stored tokens
```

### Token Storage Interface

Implement this interface to handle token persistence in your environment:

```typescript
interface TokenStorage {
  /** Provide the stored tokens to the SDK */
  getTokens(): Promise<Tokens | null>
  /** Store new or updated tokens when the SDK has successfully authenticated or refreshed tokens */
  setTokens(tokens: Tokens): Promise<void>
  /** Clear the tokens when the user logs out or the refresh token is invalid */
  clearTokens(): Promise<void>
}

type Tokens = {
  /** The workspace ID that these tokens are valid for (extracted from the access token) */
  workspaceId: string
  /** The access token for API requests */
  accessToken: string
  /** The refresh token for obtaining new access tokens (only present if scope includes 'offline_access') */
  refreshToken?: string
}
```

#### Example: VS Code Extension

```typescript
const tokenStorage: TokenStorage = {
  async getTokens() {
    const workspaceId = await context.secrets.get('workspaceId')
    const accessToken = await context.secrets.get('accessToken')
    const refreshToken = await context.secrets.get('refreshToken')
    if (!workspaceId || !accessToken) return null
    return { workspaceId, accessToken, refreshToken: refreshToken || undefined }
  },
  async setTokens(tokens) {
    await context.secrets.store('workspaceId', tokens.workspaceId)
    await context.secrets.store('accessToken', tokens.accessToken)
    if (tokens.refreshToken) {
      await context.secrets.store('refreshToken', tokens.refreshToken)
    }
  },
  async clearTokens() {
    await context.secrets.delete('workspaceId')
    await context.secrets.delete('accessToken')
    await context.secrets.delete('refreshToken')
  },
}
```

#### Example: Node.js CLI

```typescript
import { readFile, writeFile, unlink } from 'node:fs/promises'
import { homedir } from 'node:os'
import { join } from 'node:path'

const tokenPath = join(homedir(), '.prisma', 'credentials.json')

const tokenStorage: TokenStorage = {
  async getTokens() {
    try {
      const data = await readFile(tokenPath, 'utf-8')
      return JSON.parse(data)
    } catch {
      return null
    }
  },
  async setTokens(tokens) {
    await writeFile(tokenPath, JSON.stringify(tokens, null, 2))
  },
  async clearTokens() {
    await unlink(tokenPath).catch(() => {})
  },
}
```

#### Example: Stateless Web Server

For stateless web servers (serverless, load-balanced), store the PKCE state in an encrypted cookie or database:

```typescript
// In your login route
app.get('/login', async (req, res) => {
  const { url, state, verifier } = await api.getLoginUrl({
    scope: 'workspace:admin offline_access',
  })

  // Store in encrypted cookie or database keyed by state
  res.cookie('oauth-verifier', verifier, { httpOnly: true, secure: true, signed: true })
  res.cookie('oauth-state', state, { httpOnly: true, secure: true, signed: true })

  res.redirect(url)
})

// In your callback route
app.get('/callback', async (req, res) => {
  const verifier = req.signedCookies['oauth-verifier']
  const expectedState = req.signedCookies['oauth-state']

  // Clear cookies
  res.clearCookie('oauth-verifier')
  res.clearCookie('oauth-state')

  await api.handleCallback({ callbackUrl: req.url, verifier, expectedState })
  // Tokens are now stored in tokenStorage
  // ... handle successful login
})
```

### Automatic Token Refresh

The SDK automatically handles token refresh when a refresh token is available (requires `offline_access` scope):

- When a request returns 401, the SDK refreshes the access token using the refresh token
- Concurrent requests during refresh are queued and resolved once refresh completes
- If refresh fails due to an invalid refresh token, tokens are cleared and `AuthError` is thrown with `refreshTokenInvalid: true`
- If no refresh token is available, an `AuthError` is thrown with the message "No refresh token available. Please log in again."

## API Reference

### `createManagementApiClient(options)`

Creates a raw API client without authentication handling. Useful if you want to manage authentication yourself or use a service token.

**Parameters:**

- `options.token?: string` - Access token (automatically converted to `Authorization: Bearer ${token}` header)
- `options.baseUrl?: string` - Base URL for API requests (defaults to `https://api.prisma.io`)
- `options.headers?: Record<string, string>` - Additional headers
- Other `ClientOptions` from `openapi-fetch` are also supported

**Returns:** A typed API client for making requests.

### `createManagementApiSdk(config)`

Creates a Management API SDK instance with OAuth authentication and automatic token refresh.

**Parameters:**

```typescript
type ManagementApiClientConfig = {
  // Required
  clientId: string // OAuth client ID
  redirectUri: string // OAuth redirect URI
  tokenStorage: TokenStorage

  // Optional (with defaults)
  apiBaseUrl?: string // Default: 'https://api.prisma.io'
  authBaseUrl?: string // Default: 'https://auth.prisma.io'
}
```

**Returns:** An object with:

- `client` - The typed API client for making requests
- `getLoginUrl(options)` - Generate OAuth login URL with specified scope
- `handleCallback(options)` - Handle OAuth callback and store tokens via `tokenStorage`
- `logout()` - Clear stored tokens

## Error Handling

The SDK exports two error classes:

### `AuthError`

Thrown for authentication-related errors:

- OAuth callback errors (includes `error_description` when available)
- Invalid or missing tokens
- Token refresh failures

```typescript
import { AuthError } from '@prisma/management-api-sdk'

try {
  await api.handleCallback({ callbackUrl, verifier, expectedState })
} catch (error) {
  if (error instanceof AuthError) {
    if (error.refreshTokenInvalid) {
      // Token is invalid/expired, user needs to log in again
      const { url } = await api.getLoginUrl({ scope: 'workspace:admin offline_access' })
      // redirect to url...
    } else {
      // Other auth errors (e.g., "access_denied: User cancelled")
      console.error('Auth error:', error.message)
    }
  }
}
```

### `FetchError`

Thrown for network-related errors. Includes the original error as `cause` for debugging:

```typescript
import { FetchError } from '@prisma/management-api-sdk'

try {
  const { data } = await client.GET('/v1/workspaces')
} catch (error) {
  if (error instanceof FetchError) {
    console.error('Network error:', error.message)
    console.error('Cause:', error.cause) // Original error for debugging
  }
}
```

## TypeScript Types

The SDK exports all API types generated from the OpenAPI spec:

```typescript
import type { paths, components } from '@prisma/management-api-sdk'

// Access response types
type Workspace = components['schemas']['Workspace']
type Project = components['schemas']['Project']
```

## Regenerating API Types

The API types are generated from the OpenAPI spec:

```bash
pnpm generate-api-client
```

This fetches the latest spec from `https://api.prisma.io/v1/doc` and generates `src/api.d.ts`.