Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions content/250-postgres/100-introduction/230-management-api.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@ metaDescription: 'Management API reference documentation for Prisma Postgres.'

This page covers the Prisma Management API which enables you to programmatically manage [platform](/platform/about) resources (e.g. projects or Prisma Postgres instances) in [Prisma Console](https://console.prisma.io).

:::tip TypeScript SDK
The [`@prisma/management-api-sdk`](/postgres/introduction/management-api-sdk) provides a typed TypeScript client with built-in OAuth authentication and automatic token refresh. It's the recommended way to interact with the Management API in TypeScript/JavaScript applications.
:::

:::tip OpenAPI
An interactive [**OpenAPI 3.1 specification** is available here](https://api.prisma.io/v1/swagger-editor), where you can explore endpoints, request/response bodies, and detailed examples.
:::
Expand Down
371 changes: 371 additions & 0 deletions content/250-postgres/100-introduction/235-management-api-sdk.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,371 @@
---
title: 'Management API SDK'
metaTitle: 'Prisma Postgres: Management API SDK'
metaDescription: 'A TypeScript SDK for the Prisma Data Platform Management API with built-in OAuth authentication and automatic token refresh.'
---

## Overview

The [`@prisma/management-api-sdk`](https://www.npmjs.com/package/@prisma/management-api-sdk) is a TypeScript SDK for the [Prisma Data Platform Management API](/postgres/introduction/management-api) with built-in OAuth authentication and automatic token refresh.

## Installation

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

## Quick start

```typescript
import { createManagementAPI, 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 API instance
const api = createManagementAPI({
clientId: 'your-oauth-client-id',
redirectUri: 'https://your-app.com/auth/callback',
tokenStorage,
})

// Use the typed API client
const { data, error } = await api.client.GET('/v1/workspaces')
```

## 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. 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
```

### 2. 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)
}
}
```

### 3. 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' },
})
```

### 4. Logout

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

## API reference

### `createManagementAPI(config)`

Creates a Management API instance with authentication handling.

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

const api = createManagementAPI({
clientId: 'your-oauth-client-id',
redirectUri: 'https://your-app.com/auth/callback',
tokenStorage,
apiBaseUrl: 'https://api.prisma.io', // optional
authBaseUrl: 'https://auth.prisma.io', // optional
})
```

Returns an object with:

| Property | Description |
|----------|-------------|
| `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 |

### `createManagementAPIClient(options)`

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

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

const client = createManagementAPIClient({
baseUrl: 'https://api.prisma.io',
headers: {
Authorization: `Bearer ${myToken}`,
},
})

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

### Configuration options

```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'
}
```

### 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
}
```

## Examples

### 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')
},
}
```

### 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(() => {})
},
}
```

### 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
})
```

## 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']
```

## 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 api.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
}
}
```

## 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."`
Loading