Skip to content

Commit 3fb71b5

Browse files
committed
Simplify rest-api-sdk flag update API surface
1 parent 506d07a commit 3fb71b5

4 files changed

Lines changed: 211 additions & 237 deletions

File tree

packages/rest-api-sdk/README.md

Lines changed: 76 additions & 216 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# @reflag/rest-api-sdk
22

3-
Type-safe REST API client for the Reflag API.
3+
Typed Node/browser SDK for Reflag's REST API.
44

55
## Installation
66

@@ -10,30 +10,47 @@ npm install @reflag/rest-api-sdk
1010
yarn add @reflag/rest-api-sdk
1111
```
1212

13-
## Quick Start
13+
## Create a client
1414

15-
All API requests require a Bearer token. Get your API key from the Reflag dashboard.
15+
All requests require a Reflag API key.
1616

1717
```typescript
1818
import { Api } from "@reflag/rest-api-sdk";
1919

2020
const api = new Api({
2121
accessToken: process.env.REFLAG_API_KEY,
22+
// Optional when using non-default host:
23+
// basePath: "https://app.reflag.com/api",
2224
});
25+
```
26+
27+
## Quick start
2328

29+
```typescript
2430
const apps = await api.listApps();
25-
// {
26-
// data: [{ id, name, demo, flagKeyFormat, org: { id, name } }]
27-
// }
2831
console.log(apps.data);
32+
33+
const app = apps.data[0];
34+
const appId = app?.id;
35+
36+
if (appId) {
37+
const environments = await api.listEnvironments({
38+
appId,
39+
sortBy: "order",
40+
sortOrder: "asc",
41+
});
42+
43+
console.log(environments.data);
44+
}
2945
```
3046

31-
Most methods take an `appId`, or you can create an app-scoped client to avoid passing it in:
47+
## App-scoped client
48+
49+
If most calls are for one app, use `createAppClient` to avoid repeating `appId`.
3250

3351
```typescript
3452
import { createAppClient } from "@reflag/rest-api-sdk";
3553

36-
// App-scoped client keeps appId out of each call.
3754
const appApi = createAppClient("app-123", {
3855
accessToken: process.env.REFLAG_API_KEY,
3956
});
@@ -42,268 +59,111 @@ const environments = await appApi.listEnvironments({
4259
sortBy: "order",
4360
sortOrder: "asc",
4461
});
45-
// {
46-
// data: [{ id, name, isProduction, order }],
47-
// sortBy,
48-
// sortOrder
49-
// }
50-
console.log(environments.data);
51-
```
52-
53-
## API Methods
54-
55-
### Applications
56-
57-
```typescript
58-
import { Api } from "@reflag/rest-api-sdk";
59-
60-
const api = new Api();
61-
62-
// List all apps
63-
const apps = await api.listApps();
64-
// {
65-
// data: [{ id, name, demo, flagKeyFormat, org: { id, name } }]
66-
// }
67-
68-
// Filter by organization
69-
const appsByOrg = await api.listApps({ orgId: "org-123" });
70-
// same return shape as listApps()
7162

72-
// Get a single app
73-
const app = await api.getApp({
74-
appId: "app-123",
75-
});
76-
// {
77-
// id,
78-
// name,
79-
// demo,
80-
// flagKeyFormat,
81-
// environments: [{ id, name, isProduction, order, sdkAccess }],
82-
// stages: [{ id, name, color, assignedFlagCount, order }],
83-
// segments: [{ id, name, type }],
84-
// org: { id, name }
85-
// }
63+
const flags = await appApi.listFlags({});
8664
```
8765

88-
### Environments
66+
## Common workflows
8967

90-
```typescript
91-
import { createAppClient } from "@reflag/rest-api-sdk";
68+
### Read user flags for an environment
9269

93-
const appApi = createAppClient("app-123");
94-
95-
// App-scoped client (appId is implicit)
96-
const environments = await appApi.listEnvironments({
97-
sortBy: "order",
98-
sortOrder: "asc",
99-
});
100-
// {
101-
// data: [{ id, name, isProduction, order }],
102-
// sortBy,
103-
// sortOrder
104-
// }
70+
`getUserFlags` evaluates flag results for one user in one environment and returns
71+
the user’s current values plus exposure/check metadata for each flag.
10572

106-
const environment = await appApi.getEnvironment({
73+
```typescript
74+
const userFlags = await api.getUserFlags({
75+
appId: "app-123",
10776
envId: "env-456",
77+
userId: "user-1",
10878
});
109-
// {
110-
// id,
111-
// name,
112-
// isProduction,
113-
// order,
114-
// sdkAccess: { publishableKey, secretKey }
115-
// }
79+
80+
console.log(userFlags.data);
11681
```
11782

118-
### Flags
83+
### Toggle a user flag
84+
85+
Use `true` to explicitly target on, and `null` to remove specific targeting.
11986

12087
```typescript
121-
const flags = await api.listFlags({ appId: "app-123" });
122-
// {
123-
// data: [
124-
// {
125-
// id,
126-
// key,
127-
// name,
128-
// description?,
129-
// stage?,
130-
// owner?,
131-
// archived,
132-
// stale,
133-
// permanent,
134-
// createdAt?,
135-
// lastCheckAt?,
136-
// lastTrackAt?
137-
// }
138-
// ],
139-
// totalCount,
140-
// pageSize,
141-
// pageIndex,
142-
// sortBy,
143-
// sortOrder
144-
// }
145-
146-
const targeting = await api.getFlagTargeting({
147-
appId: "app-123",
148-
flagKey: "my-feature-flag",
149-
envId: "env-456",
150-
});
151-
// {
152-
// flagKey,
153-
// version,
154-
// updatedAt,
155-
// specificTargets: {
156-
// // Currently only "true" exists because this API supports the true variant.
157-
// "true": { companyIds, userIds }
158-
// }
159-
// }
160-
161-
const updated = await api.updateBulkFlagSpecificTargets({
88+
await api.updateUserFlags({
16289
appId: "app-123",
16390
envId: "env-456",
164-
bulkUpdateFlagSpecificTargetsSchema: {
165-
updates: [
166-
{ flagKey: "new-feature", value: true, companyId: "company-1" },
167-
{ flagKey: "new-feature", value: true, userId: "user-1" },
168-
{ flagKey: "old-feature", value: null, companyId: "company-1" },
169-
],
170-
notifications: true,
171-
changeDescription: "Enabling new feature for beta testers",
172-
},
91+
userId: "user-1",
92+
updates: [{ flagKey: "new-checkout", value: true }],
17393
});
174-
// {
175-
// data: [{ flagKey, version, updatedAt, specificTargets }]
176-
// }
17794
```
17895

179-
### Company Flags
96+
### Read and update company flags
18097

18198
```typescript
18299
const companyFlags = await api.getCompanyFlags({
183100
appId: "app-123",
184-
companyId: "company-1",
185101
envId: "env-456",
102+
companyId: "company-1",
186103
});
187-
// {
188-
// data: [
189-
// {
190-
// id,
191-
// key,
192-
// name,
193-
// value,
194-
// specificallyTargetedValue,
195-
// firstExposureAt,
196-
// lastExposureAt,
197-
// lastCheckAt,
198-
// exposureCount,
199-
// firstTrackAt,
200-
// lastTrackAt,
201-
// trackCount
202-
// }
203-
// ],
204-
// totalCount,
205-
// pageSize,
206-
// pageIndex
207-
// }
208-
209-
const updatedCompanyFlags = await api.updateCompanyFlags({
104+
105+
await api.updateCompanyFlags({
210106
appId: "app-123",
211-
companyId: "company-1",
212107
envId: "env-456",
213-
updateEntityFlagsBody: {
214-
updates: [
215-
{ flagKey: "feature-flag", value: true },
216-
{ flagKey: "another-flag", value: null },
217-
],
218-
},
108+
companyId: "company-1",
109+
updates: [{ flagKey: "new-checkout", value: null }],
219110
});
220-
// same return shape as getCompanyFlags()
221111
```
222112

223-
### User Flags
113+
### Bulk update specific targets for multiple flags
224114

225115
```typescript
226-
const userFlags = await api.getUserFlags({
227-
appId: "app-123",
228-
userId: "user-1",
229-
envId: "env-456",
230-
});
231-
// {
232-
// data: [
233-
// {
234-
// id,
235-
// key,
236-
// name,
237-
// value,
238-
// specificallyTargetedValue,
239-
// firstExposureAt,
240-
// lastExposureAt,
241-
// lastCheckAt,
242-
// exposureCount,
243-
// firstTrackAt,
244-
// lastTrackAt,
245-
// trackCount
246-
// }
247-
// ],
248-
// totalCount,
249-
// pageSize,
250-
// pageIndex
251-
// }
252-
253-
const updatedUserFlags = await api.updateUserFlags({
116+
await api.updateBulkFlagSpecificTargets({
254117
appId: "app-123",
255-
userId: "user-1",
256118
envId: "env-456",
257-
updateEntityFlagsBody: {
119+
bulkUpdateFlagSpecificTargetsSchema: {
258120
updates: [
259-
{ flagKey: "feature-flag", value: true },
260-
{ flagKey: "beta-feature", value: true },
121+
{ flagKey: "new-checkout", value: true, companyId: "company-1" },
122+
{ flagKey: "new-checkout", value: true, userId: "user-1" },
123+
{ flagKey: "legacy-checkout", value: null, userId: "user-1" },
261124
],
125+
notifications: true,
126+
changeDescription: "Rolling out new checkout to pilot accounts",
262127
},
263128
});
264-
// same return shape as getUserFlags()
265129
```
266130

267-
## Error Handling
131+
## Error handling
268132

269-
Methods throw on non-2xx responses. Catch errors and inspect the response when needed.
133+
The SDK throws `ReflagApiError` for non-2xx API responses.
270134

271135
```typescript
136+
import { ReflagApiError } from "@reflag/rest-api-sdk";
137+
272138
try {
273-
const apps = await api.listApps();
274-
console.log(apps.data);
139+
await api.listApps();
275140
} catch (error) {
276-
if (error instanceof Error) {
277-
console.error("Request failed", error.message);
141+
if (error instanceof ReflagApiError) {
142+
console.error(error.status, error.code, error.message, error.details);
278143
}
279144
throw error;
280145
}
281146
```
282147

283-
## Types
148+
## API surface
284149

285-
All generated types are exported for use in your application:
150+
Main exports:
286151

287-
```typescript
288-
import type {
289-
AppHeader,
290-
EnvironmentHeader,
291-
Environment,
292-
FlagHeader,
293-
FlagTargeting,
294-
ErrorResponse,
295-
} from "@reflag/rest-api-sdk";
296-
```
152+
- `Api`: base client
153+
- `createAppClient(appId, config)`: app-scoped client
154+
- `ReflagApiError`: normalized API error type
155+
- Generated request/response types and models from `@reflag/rest-api-sdk`
297156

298-
## Regenerating the SDK
157+
Core method groups:
299158

300-
To regenerate the SDK:
159+
- Applications: `listApps`, `getApp`
160+
- Environments: `listEnvironments`, `getEnvironment`
161+
- Flags: `listFlags`, `getFlagTargeting`, `updateBulkFlagSpecificTargets`
162+
- User/company evaluation: `getUserFlags`, `updateUserFlags`, `getCompanyFlags`, `updateCompanyFlags`
301163

302-
```bash
303-
yarn generate
304-
```
164+
## Example app
305165

306-
The schema source lives at `https://app.reflag.com/openapi.json`.
166+
See `packages/rest-api-sdk/examples/customer-admin-panel/README.md` for a small Next.js app using this SDK in server actions.
307167

308168
## License
309169

0 commit comments

Comments
 (0)