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
1010yarn 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
1818import { Api } from " @reflag/rest-api-sdk" ;
1919
2020const 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
2430const apps = await api .listApps ();
25- // {
26- // data: [{ id, name, demo, flagKeyFormat, org: { id, name } }]
27- // }
2831console .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
3452import { createAppClient } from " @reflag/rest-api-sdk" ;
3553
36- // App-scoped client keeps appId out of each call.
3754const 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
18299const 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+
272138try {
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