Skip to content

Commit 0ed9e8e

Browse files
add management api sdk docs (#7375)
1 parent b821fba commit 0ed9e8e

File tree

2 files changed

+375
-0
lines changed

2 files changed

+375
-0
lines changed

content/250-postgres/100-introduction/230-management-api.mdx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,10 @@ metaDescription: 'Management API reference documentation for Prisma Postgres.'
88

99
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).
1010

11+
:::tip TypeScript SDK
12+
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.
13+
:::
14+
1115
:::tip OpenAPI
1216
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.
1317
:::
Lines changed: 371 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,371 @@
1+
---
2+
title: 'Management API SDK'
3+
metaTitle: 'Prisma Postgres: Management API SDK'
4+
metaDescription: 'A TypeScript SDK for the Prisma Data Platform Management API with built-in OAuth authentication and automatic token refresh.'
5+
---
6+
7+
## Overview
8+
9+
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.
10+
11+
## Installation
12+
13+
```terminal
14+
npm install @prisma/management-api-sdk
15+
```
16+
17+
## Quick start
18+
19+
```typescript
20+
import { createManagementAPI, type TokenStorage } from '@prisma/management-api-sdk'
21+
22+
// Implement token storage for your environment
23+
const tokenStorage: TokenStorage = {
24+
async getTokens() {
25+
const stored = localStorage.getItem('prisma-tokens')
26+
return stored ? JSON.parse(stored) : null
27+
},
28+
async setTokens(tokens) {
29+
localStorage.setItem('prisma-tokens', JSON.stringify(tokens))
30+
},
31+
async clearTokens() {
32+
localStorage.removeItem('prisma-tokens')
33+
},
34+
}
35+
36+
// Create the API instance
37+
const api = createManagementAPI({
38+
clientId: 'your-oauth-client-id',
39+
redirectUri: 'https://your-app.com/auth/callback',
40+
tokenStorage,
41+
})
42+
43+
// Use the typed API client
44+
const { data, error } = await api.client.GET('/v1/workspaces')
45+
```
46+
47+
## Authentication flow
48+
49+
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.
50+
51+
### 1. Initiate login
52+
53+
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:
54+
55+
```typescript
56+
const { url, state, verifier } = await api.getLoginUrl({
57+
scope: 'workspace:admin offline_access',
58+
additionalParams: {
59+
utm_source: 'my-app',
60+
utm_medium: 'login',
61+
},
62+
})
63+
64+
// Store state and verifier for the callback (e.g., in session storage)
65+
sessionStorage.setItem('oauth-state', state)
66+
sessionStorage.setItem('oauth-verifier', verifier)
67+
68+
// Redirect user to the login URL
69+
window.location.href = url
70+
```
71+
72+
### 2. Handle the callback
73+
74+
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:
75+
76+
```typescript
77+
// In your callback route handler
78+
const callbackUrl = window.location.href
79+
80+
// Retrieve the stored values
81+
const expectedState = sessionStorage.getItem('oauth-state')
82+
const verifier = sessionStorage.getItem('oauth-verifier')
83+
84+
// Clean up stored values
85+
sessionStorage.removeItem('oauth-state')
86+
sessionStorage.removeItem('oauth-verifier')
87+
88+
try {
89+
await api.handleCallback({
90+
callbackUrl,
91+
verifier,
92+
expectedState,
93+
})
94+
95+
// Tokens are now stored in tokenStorage and the client is ready to use
96+
console.log('Login successful!')
97+
} catch (error) {
98+
if (error instanceof AuthError) {
99+
console.error('Authentication failed:', error.message)
100+
}
101+
}
102+
```
103+
104+
### 3. Make API calls
105+
106+
The client automatically includes authentication headers and refreshes tokens when they expire:
107+
108+
```typescript
109+
// List workspaces
110+
const { data: workspaces } = await api.client.GET('/v1/workspaces')
111+
112+
// Get a specific project
113+
const { data: project } = await api.client.GET('/v1/projects/{id}', {
114+
params: { path: { id: 'project-id' } },
115+
})
116+
117+
// Create a new project
118+
const { data: newProject } = await api.client.POST('/v1/workspaces/{workspaceId}/projects', {
119+
params: { path: { workspaceId: 'workspace-id' } },
120+
body: { name: 'My Project' },
121+
})
122+
```
123+
124+
### 4. Logout
125+
126+
```typescript
127+
await api.logout() // Clears stored tokens
128+
```
129+
130+
## API reference
131+
132+
### `createManagementAPI(config)`
133+
134+
Creates a Management API instance with authentication handling.
135+
136+
```typescript
137+
import { createManagementAPI } from '@prisma/management-api-sdk'
138+
139+
const api = createManagementAPI({
140+
clientId: 'your-oauth-client-id',
141+
redirectUri: 'https://your-app.com/auth/callback',
142+
tokenStorage,
143+
apiBaseUrl: 'https://api.prisma.io', // optional
144+
authBaseUrl: 'https://auth.prisma.io', // optional
145+
})
146+
```
147+
148+
Returns an object with:
149+
150+
| Property | Description |
151+
|----------|-------------|
152+
| `client` | The typed API client for making requests |
153+
| `getLoginUrl(options)` | Generate OAuth login URL with specified scope |
154+
| `handleCallback(options)` | Handle OAuth callback and store tokens via tokenStorage |
155+
| `logout()` | Clear stored tokens |
156+
157+
### `createManagementAPIClient(options)`
158+
159+
Creates a raw API client without authentication handling. Useful if you want to manage authentication yourself.
160+
161+
```typescript
162+
import { createManagementAPIClient } from '@prisma/management-api-sdk'
163+
164+
const client = createManagementAPIClient({
165+
baseUrl: 'https://api.prisma.io',
166+
headers: {
167+
Authorization: `Bearer ${myToken}`,
168+
},
169+
})
170+
171+
const { data } = await client.GET('/v1/workspaces')
172+
```
173+
174+
### Configuration options
175+
176+
```typescript
177+
type ManagementAPIClientConfig = {
178+
// Required
179+
clientId: string // OAuth client ID
180+
redirectUri: string // OAuth redirect URI
181+
tokenStorage: TokenStorage
182+
183+
// Optional (with defaults)
184+
apiBaseUrl?: string // Default: 'https://api.prisma.io'
185+
authBaseUrl?: string // Default: 'https://auth.prisma.io'
186+
}
187+
```
188+
189+
### Token storage interface
190+
191+
Implement this interface to handle token persistence in your environment:
192+
193+
```typescript
194+
interface TokenStorage {
195+
/** Provide the stored tokens to the SDK */
196+
getTokens(): Promise<Tokens | null>
197+
/** Store new or updated tokens when the SDK has successfully authenticated or refreshed tokens */
198+
setTokens(tokens: Tokens): Promise<void>
199+
/** Clear the tokens when the user logs out or the refresh token is invalid */
200+
clearTokens(): Promise<void>
201+
}
202+
203+
type Tokens = {
204+
/** The workspace ID that these tokens are valid for (extracted from the access token) */
205+
workspaceId: string
206+
/** The access token for API requests */
207+
accessToken: string
208+
/** The refresh token for obtaining new access tokens (only present if scope includes 'offline_access') */
209+
refreshToken?: string
210+
}
211+
```
212+
213+
## Examples
214+
215+
### VS Code extension
216+
217+
```typescript
218+
const tokenStorage: TokenStorage = {
219+
async getTokens() {
220+
const workspaceId = await context.secrets.get('workspaceId')
221+
const accessToken = await context.secrets.get('accessToken')
222+
const refreshToken = await context.secrets.get('refreshToken')
223+
224+
if (!workspaceId || !accessToken) return null
225+
226+
return { workspaceId, accessToken, refreshToken: refreshToken || undefined }
227+
},
228+
async setTokens(tokens) {
229+
await context.secrets.store('workspaceId', tokens.workspaceId)
230+
await context.secrets.store('accessToken', tokens.accessToken)
231+
if (tokens.refreshToken) {
232+
await context.secrets.store('refreshToken', tokens.refreshToken)
233+
}
234+
},
235+
async clearTokens() {
236+
await context.secrets.delete('workspaceId')
237+
await context.secrets.delete('accessToken')
238+
await context.secrets.delete('refreshToken')
239+
},
240+
}
241+
```
242+
243+
### Node.js CLI
244+
245+
```typescript
246+
import { readFile, writeFile, unlink } from 'node:fs/promises'
247+
import { homedir } from 'node:os'
248+
import { join } from 'node:path'
249+
250+
const tokenPath = join(homedir(), '.prisma', 'credentials.json')
251+
252+
const tokenStorage: TokenStorage = {
253+
async getTokens() {
254+
try {
255+
const data = await readFile(tokenPath, 'utf-8')
256+
return JSON.parse(data)
257+
} catch {
258+
return null
259+
}
260+
},
261+
async setTokens(tokens) {
262+
await writeFile(tokenPath, JSON.stringify(tokens, null, 2))
263+
},
264+
async clearTokens() {
265+
await unlink(tokenPath).catch(() => {})
266+
},
267+
}
268+
```
269+
270+
### Stateless web server
271+
272+
For stateless web servers (serverless, load-balanced), store the PKCE state in an encrypted cookie or database:
273+
274+
```typescript
275+
// In your login route
276+
app.get('/login', async (req, res) => {
277+
const { url, state, verifier } = await api.getLoginUrl({
278+
scope: 'workspace:admin offline_access',
279+
})
280+
281+
// Store in encrypted cookie or database keyed by state
282+
res.cookie('oauth-verifier', verifier, { httpOnly: true, secure: true, signed: true })
283+
res.cookie('oauth-state', state, { httpOnly: true, secure: true, signed: true })
284+
285+
res.redirect(url)
286+
})
287+
288+
// In your callback route
289+
app.get('/callback', async (req, res) => {
290+
const verifier = req.signedCookies['oauth-verifier']
291+
const expectedState = req.signedCookies['oauth-state']
292+
293+
// Clear cookies
294+
res.clearCookie('oauth-verifier')
295+
res.clearCookie('oauth-state')
296+
297+
await api.handleCallback({ callbackUrl: req.url, verifier, expectedState })
298+
299+
// Tokens are now stored in tokenStorage
300+
// ... handle successful login
301+
})
302+
```
303+
304+
## TypeScript types
305+
306+
The SDK exports all API types generated from the OpenAPI spec:
307+
308+
```typescript
309+
import type { paths, components } from '@prisma/management-api-sdk'
310+
311+
// Access response types
312+
type Workspace = components['schemas']['Workspace']
313+
type Project = components['schemas']['Project']
314+
```
315+
316+
## Error handling
317+
318+
The SDK exports two error classes:
319+
320+
### `AuthError`
321+
322+
Thrown for authentication-related errors:
323+
324+
- OAuth callback errors (includes `error_description` when available)
325+
- Invalid or missing tokens
326+
- Token refresh failures
327+
328+
```typescript
329+
import { AuthError } from '@prisma/management-api-sdk'
330+
331+
try {
332+
await api.handleCallback({ callbackUrl, verifier, expectedState })
333+
} catch (error) {
334+
if (error instanceof AuthError) {
335+
if (error.refreshTokenInvalid) {
336+
// Token is invalid/expired, user needs to log in again
337+
const { url } = await api.getLoginUrl({ scope: 'workspace:admin offline_access' })
338+
// redirect to url...
339+
} else {
340+
// Other auth errors (e.g., "access_denied: User cancelled")
341+
console.error('Auth error:', error.message)
342+
}
343+
}
344+
}
345+
```
346+
347+
### `FetchError`
348+
349+
Thrown for network-related errors. Includes the original error as `cause` for debugging:
350+
351+
```typescript
352+
import { FetchError } from '@prisma/management-api-sdk'
353+
354+
try {
355+
const { data } = await api.client.GET('/v1/workspaces')
356+
} catch (error) {
357+
if (error instanceof FetchError) {
358+
console.error('Network error:', error.message)
359+
console.error('Cause:', error.cause) // Original error for debugging
360+
}
361+
}
362+
```
363+
364+
## Automatic token refresh
365+
366+
The SDK automatically handles token refresh when a refresh token is available (requires `offline_access` scope):
367+
368+
- When a request returns `401`, the SDK refreshes the access token using the refresh token
369+
- Concurrent requests during refresh are queued and resolved once refresh completes
370+
- If refresh fails due to an invalid refresh token, tokens are cleared and `AuthError` is thrown with `refreshTokenInvalid: true`
371+
- If no refresh token is available, an `AuthError` is thrown with the message `"No refresh token available. Please log in again."`

0 commit comments

Comments
 (0)