quickbase-js 2.4.0 → 2.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. package/README.md +144 -76
  2. package/dist/quickbase.esm.js +3 -3
  3. package/dist/quickbase.min.js +3 -3
  4. package/dist/quickbase.min.js.map +1 -1
  5. package/package.json +13 -9
  6. package/src/auth/index.ts +55 -0
  7. package/src/auth/sso-token.ts +143 -0
  8. package/src/auth/temp-token.ts +160 -0
  9. package/src/auth/ticket.ts +329 -0
  10. package/src/auth/token-cache.ts +97 -0
  11. package/src/auth/types.ts +50 -0
  12. package/src/auth/user-token.ts +38 -0
  13. package/src/cli/schema.ts +512 -0
  14. package/src/client/index.ts +36 -0
  15. package/src/client/pagination.ts +337 -0
  16. package/src/client/request.ts +544 -0
  17. package/src/client/retry.ts +169 -0
  18. package/src/client/throttle.ts +105 -0
  19. package/src/core/config.ts +160 -0
  20. package/src/core/dates.ts +63 -0
  21. package/src/core/errors.ts +349 -0
  22. package/src/core/index.ts +50 -0
  23. package/src/core/logger.ts +113 -0
  24. package/src/core/schema.ts +213 -0
  25. package/src/core/transform.ts +295 -0
  26. package/src/core/types.ts +337 -0
  27. package/src/generated/client.ts +1785 -0
  28. package/src/generated/operations.json +672 -0
  29. package/src/generated/types.ts +4884 -0
  30. package/src/index.ts +259 -0
  31. package/src/xml/codepages.ts +119 -0
  32. package/src/xml/dbvars.ts +75 -0
  33. package/src/xml/errors.ts +133 -0
  34. package/src/xml/fields.ts +121 -0
  35. package/src/xml/groups.ts +560 -0
  36. package/src/xml/html.ts +152 -0
  37. package/src/xml/index.ts +1196 -0
  38. package/src/xml/metadata.ts +111 -0
  39. package/src/xml/records.ts +301 -0
  40. package/src/xml/request.ts +238 -0
  41. package/src/xml/roles.ts +321 -0
  42. package/src/xml/schema.ts +436 -0
  43. package/src/xml/transform.ts +381 -0
  44. package/src/xml/types.ts +602 -0
  45. package/src/xml/users.ts +221 -0
  46. package/src/xml/webhooks.ts +260 -0
package/README.md CHANGED
@@ -1,14 +1,22 @@
1
- # QuickBase JS SDK
1
+ # quickbase-js
2
2
 
3
- A TypeScript client for the [QuickBase JSON API](https://developer.quickbase.com/), for Node.js and the browser (including QuickBase code pages), with the legacy XML API for what JSON lacks.
3
+ [![npm](https://img.shields.io/npm/v/quickbase-js)](https://www.npmjs.com/package/quickbase-js)
4
+ [![CI](https://github.com/DrewBradfordXYZ/quickbase-js/actions/workflows/ci.yml/badge.svg)](https://github.com/DrewBradfordXYZ/quickbase-js/actions/workflows/ci.yml)
5
+ [![license](https://img.shields.io/npm/l/quickbase-js)](LICENSE)
4
6
 
5
- - **Every operation** as a typed method, generated from a spec checked against live responses
6
- - **Pagination** with `.all()` and `.paginate()`, and readable **schema aliases** instead of IDs
7
- - **Temporary tokens in code pages**, fetched and refreshed for you from the user's session
8
- - **Retries and throttling** for QuickBase's rate limits
9
- - **Read-only mode** for tools that must never write
7
+ The QuickBase API for TypeScript and JavaScript, in Node.js, the browser and QuickBase code pages.
10
8
 
11
- **[API reference →](https://drewbradfordxyz.github.io/quickbase-js/)**
9
+ ```typescript
10
+ const projects = await qb.runQuery({ from: 'bqtable1', where: "{7.EX.'Active'}" }).all();
11
+ ```
12
+
13
+ - **Every endpoint** in QuickBase's API as a typed method, generated from QuickBase's published spec and checked against live responses.
14
+ - **Every page of results** with `.all()`, so you never write a `skip` loop.
15
+ - **Field names instead of IDs**: `select: ['name', 'status']`, with a generator that writes the names from your app.
16
+ - **No token in code pages**: the client signs in with the user's QuickBase session and renews its tokens.
17
+ - **Rate limits handled**: 429s and server errors are retried with backoff, and the client can pace itself to stay under the limit.
18
+ - **The XML API too**, for roles, groups, code pages, app variables and CSV imports.
19
+ - **No dependencies**, a read-only mode for tools that must never write, and typed errors.
12
20
 
13
21
  ```bash
14
22
  npm install quickbase-js
@@ -16,11 +24,13 @@ npm install quickbase-js
16
24
 
17
25
  ## Quick start
18
26
 
27
+ Create a [user token](https://help.quickbase.com/docs/user-tokens) in QuickBase, then:
28
+
19
29
  ```typescript
20
30
  import { createClient } from 'quickbase-js';
21
31
 
22
32
  const qb = createClient({
23
- realm: 'mycompany',
33
+ realm: 'mycompany', // mycompany.quickbase.com
24
34
  auth: { type: 'user-token', userToken: process.env.QB_USER_TOKEN! },
25
35
  });
26
36
 
@@ -44,7 +54,7 @@ await qb.upsert({
44
54
 
45
55
  ## In a code page
46
56
 
47
- In the browser, temporary-token auth uses the signed-in user's QuickBase session: no token in your code, and every call has that user's permissions.
57
+ Load the browser bundle and use temporary-token auth. Calls run as the signed-in user, with that user's permissions, and there is no token in your page.
48
58
 
49
59
  ```html
50
60
  <script src="https://cdn.jsdelivr.net/npm/quickbase-js@2/dist/quickbase.min.js"></script>
@@ -57,36 +67,65 @@ In the browser, temporary-token auth uses the signed-in user's QuickBase session
57
67
  </script>
58
68
  ```
59
69
 
60
- Or as a module: `import { createClient } from 'https://cdn.jsdelivr.net/npm/quickbase-js@2/dist/quickbase.esm.js'`.
70
+ As a module: `import { createClient } from 'https://cdn.jsdelivr.net/npm/quickbase-js@2/dist/quickbase.esm.js'`.
71
+
72
+ [`examples/codepages`](examples/codepages) has a complete code page for each of vanilla web components, Alpine, Lit, Preact, Solid, React and Vue. Paste one into a new code page and it explores the app it's in.
61
73
 
62
- ## Typed methods for every operation
74
+ ## Common tasks
63
75
 
64
- Each QuickBase operation is a method on the client, typed from [quickbase-spec](https://github.com/DrewBradfordXYZ/quickbase-spec), including properties QuickBase returns but doesn't document. Path and query parameters come first, then the body:
76
+ ### Get every record from a table
65
77
 
66
78
  ```typescript
67
- await qb.getFields({ tableId: 'bqtable1' });
68
- await qb.runQuery({ from: 'bqtable1', select: [3, 6], sortBy: [{ fieldId: 3, order: 'ASC' }] });
69
- await qb.deleteRecords({ from: 'bqtable1', where: '{3.EX.123}' });
70
- await qb.updateApp({ appId: 'bpqe82s1' }, { name: 'Renamed' });
79
+ const all = await qb.runQuery({ from: 'bqtable1' }).all();
80
+ const first500 = await qb.runQuery({ from: 'bqtable1' }).paginate({ limit: 500 });
81
+ const page = await qb.runQuery({ from: 'bqtable1' }); // one page
71
82
  ```
72
83
 
73
- All of them, with QuickBase's descriptions: the [API reference](https://drewbradfordxyz.github.io/quickbase-js/interfaces/QuickbaseAPI.html).
84
+ QuickBase returns a page at a time. `runQuery`, `runReport`, `getUsers` and the other paginated methods return one page when awaited, and follow the pages with `.all()` or `.paginate()`. Set `autoPaginate: true` on the client to make a plain `await` fetch them all.
74
85
 
75
- ## Pagination
86
+ ### Filter and sort
76
87
 
77
- QuickBase returns a page of records at a time. Paginated methods (`runQuery`, `runReport`, `getUsers`, ...) return one page when awaited, or follow the pages:
88
+ ```typescript
89
+ await qb.runQuery({
90
+ from: 'bqtable1',
91
+ select: [3, 6, 7],
92
+ where: '{6.GT.100}',
93
+ sortBy: [{ fieldId: 3, order: 'ASC' }],
94
+ options: { top: 100, skip: 0 },
95
+ });
96
+ ```
97
+
98
+ `where` uses QuickBase's [query syntax](https://help.quickbase.com/docs/components-of-a-query): `{field.OPERATOR.'value'}`, joined with `AND` and `OR`.
99
+
100
+ ### Run a report
78
101
 
79
102
  ```typescript
80
- const page = await qb.runQuery({ from: 'bqtable1' }); // one page
81
- const all = await qb.runQuery({ from: 'bqtable1' }).all(); // every page
82
- const first500 = await qb.runQuery({ from: 'bqtable1' }).paginate({ limit: 500 });
103
+ const reportRows = await qb.runReport({ reportId: '7', tableId: 'bqtable1' }).all();
83
104
  ```
84
105
 
85
- `autoPaginate: true` makes a plain `await` fetch every page. See [guides/queries.md](guides/queries.md).
106
+ ### Add or update records
86
107
 
87
- ## Schema aliases
108
+ `upsert` adds records, or updates the one whose merge field matches (the key field, usually Record ID#, when you don't name one):
88
109
 
89
- Give the client names for your tables and fields, and use them instead of IDs. Requests are translated to IDs; responses come back keyed by name, with values unwrapped.
110
+ ```typescript
111
+ const result = await qb.upsert({
112
+ to: 'bqtable1',
113
+ mergeFieldId: 9,
114
+ data: [{ '9': { value: 'EXT-001' }, '6': { value: 'Alpha' } }],
115
+ fieldsToReturn: [6],
116
+ });
117
+ console.log(result.metadata?.createdRecordIds, result.metadata?.updatedRecordIds);
118
+ ```
119
+
120
+ ### Delete records
121
+
122
+ ```typescript
123
+ await qb.deleteRecords({ from: 'bqtable1', where: '{3.EX.123}' });
124
+ ```
125
+
126
+ ### Use field names instead of IDs
127
+
128
+ Give the client a schema and use your names anywhere an ID goes. Records come back keyed by name, with plain values:
90
129
 
91
130
  ```typescript
92
131
  const qb = createClient({
@@ -100,10 +139,63 @@ const result = await qb.runQuery({
100
139
  select: ['name', 'status'],
101
140
  where: "{'status'.EX.'Active'}",
102
141
  });
103
- console.log(result.data?.[0]?.name);
142
+ const first = result.data?.[0] as Record<string, unknown> | undefined; // TypeScript: keyed by name
143
+ console.log(first?.name, first?.status);
144
+
145
+ await qb.upsert({ to: 'projects', data: [{ name: 'Alpha', status: 'Active' }] });
146
+ ```
147
+
148
+ Generate the schema from your app instead of writing it, and regenerate with `--merge` when the app changes (your renamed aliases are kept):
149
+
150
+ ```bash
151
+ npx quickbase-js schema -r mycompany -a bpqe82s1 -t "$QB_USER_TOKEN" -o schema.ts
152
+ ```
153
+
154
+ A misspelled name throws with a suggestion (`Did you mean 'status'?`). Results are typed by field ID, which is why TypeScript needs the cast; the [schema guide](guides/schema-aliases.md) has the details.
155
+
156
+ ### Read an app's tables and fields
157
+
158
+ ```typescript
159
+ const tables = await qb.getAppTables({ appId: 'bpqe82s1' });
160
+ console.log(tables.map((t) => `${t.id} ${t.name}`));
161
+
162
+ const fields = await qb.getFields({ tableId: 'bqtable1' });
163
+ console.log(fields.map((f) => `${f.id} ${f.label} (${f.fieldType})`));
104
164
  ```
105
165
 
106
- Requests accept aliases in TypeScript too. Results are typed by field ID, so read an aliased record with a cast. See [guides/schema-aliases.md](guides/schema-aliases.md), which also covers the schema generator (`npx quickbase-js schema`).
166
+ ### Export and update a solution, download a file
167
+
168
+ Solutions are QBL (YAML) and files are raw content, so these take and return text:
169
+
170
+ ```typescript
171
+ const qbl = await qb.exportSolution({ solutionId: 'solution-id' });
172
+ await qb.updateSolution({ solutionId: 'solution-id' }, qbl);
173
+ const file = await qb.downloadFile({ tableId: 'bqtable1', recordId: 12, fieldId: 8, versionNumber: 0 }); // base64
174
+ ```
175
+
176
+ ### Manage roles, groups and code pages
177
+
178
+ The JSON API doesn't cover these, so `createXmlClient(qb)` calls the XML API with the same authentication, retries, read-only mode and schema:
179
+
180
+ ```typescript
181
+ import { createXmlClient } from 'quickbase-js';
182
+
183
+ const xml = createXmlClient(qb);
184
+ const roles = await xml.getRoleInfo(appId);
185
+ for (const role of roles.roles) {
186
+ console.log(role.name, role.access.description);
187
+ }
188
+ ```
189
+
190
+ Every method is in the [XML API guide](guides/xml-api.md).
191
+
192
+ ### Call any endpoint directly
193
+
194
+ ```typescript
195
+ const result = await qb.request<{ id: string }>({ method: 'GET', path: '/apps/bpqe82s1' });
196
+ ```
197
+
198
+ `request` uses the client's authentication, retries and throttling.
107
199
 
108
200
  ## Authentication
109
201
 
@@ -114,11 +206,11 @@ Requests accept aliases in TypeScript too. Results are typed by field ID, so rea
114
206
  | `'ticket'` | A user signing in with their QuickBase email and password |
115
207
  | `'sso'` | A user signed in through your identity provider (SAML) |
116
208
 
117
- Details: [guides/authentication.md](guides/authentication.md).
209
+ Details are in the [authentication guide](guides/authentication.md).
118
210
 
119
211
  ## Rate limits and retries
120
212
 
121
- QuickBase allows 100 requests per 10 seconds per user token. The client retries 429s and server errors with exponential backoff (honoring `Retry-After`) and can throttle itself so it never hits the limit:
213
+ QuickBase allows 100 requests per 10 seconds per user token. The client retries 429s and server errors with exponential backoff, honoring `Retry-After`, and can throttle itself so it never hits the limit:
122
214
 
123
215
  ```typescript
124
216
  createClient({
@@ -131,14 +223,12 @@ createClient({
131
223
  });
132
224
  ```
133
225
 
134
- See [guides/reliability.md](guides/reliability.md).
135
-
136
- ## Read-only mode
137
-
138
- `readOnly: true` makes the client refuse every write before it is sent, for the JSON API and the XML client made from it. Reads that use POST, like `runQuery`, `runReport` and `audit`, still work. A blocked call throws `ReadOnlyError`.
226
+ Timeouts, backoff settings and a rate-limit callback are in the [reliability guide](guides/reliability.md).
139
227
 
140
228
  ## Errors
141
229
 
230
+ Every error extends `QuickbaseError`, with a class for each kind of failure:
231
+
142
232
  ```typescript
143
233
  import { RateLimitError, NotFoundError, ReadOnlyError, QuickbaseError } from 'quickbase-js';
144
234
 
@@ -157,51 +247,29 @@ try {
157
247
  }
158
248
  ```
159
249
 
160
- The classes are `RateLimitError` (429), `AuthenticationError` (401), `AuthorizationError` (403), `NotFoundError` (404), `ValidationError` (400), `ServerError` (5xx) and `ReadOnlyError`, all extending `QuickbaseError`; the XML API adds `QuickbaseTicketError`, `QuickbaseAppTokenError` and `QuickbaseXmlError`.
161
-
162
- ## Solutions and files
163
-
164
- Solutions are QBL (YAML) and file downloads are raw content, so these methods take and return text:
165
-
166
- ```typescript
167
- const qbl = await qb.exportSolution({ solutionId: 'solution-id' });
168
- await qb.updateSolution({ solutionId: 'solution-id' }, qbl);
169
- const file = await qb.downloadFile({ tableId: 'bqtable1', recordId: 12, fieldId: 8, versionNumber: 0 }); // base64
170
- ```
171
-
172
- ## Dates
173
-
174
- ISO date strings in responses become `Date` objects. The generated types say `string`, because QuickBase's spec marks few of its date fields as dates; set `convertDates: false` if you want values to match the types exactly.
175
-
176
- ## Raw requests
177
-
178
- `qb.request<T>({ method, path, body, query })` calls any endpoint with the client's authentication, retries and throttling.
179
-
180
- ## The legacy XML API
181
-
182
- `createXmlClient(qb)` reaches what the JSON API doesn't: roles, groups, code pages, app variables, webhooks, CSV imports. It shares the client's authentication, retries, read-only mode and schema.
250
+ | Class | Status |
251
+ |---|---|
252
+ | `ValidationError` | 400 |
253
+ | `AuthenticationError` | 401 |
254
+ | `AuthorizationError` | 403 |
255
+ | `NotFoundError` | 404 |
256
+ | `RateLimitError` | 429 |
257
+ | `ServerError` | 5xx |
258
+ | `ReadOnlyError` | a write blocked by `readOnly: true` |
183
259
 
184
- ```typescript
185
- import { createXmlClient } from 'quickbase-js';
260
+ The XML API adds `QuickbaseTicketError`, `QuickbaseAppTokenError` and `QuickbaseXmlError`.
186
261
 
187
- const xml = createXmlClient(qb);
188
- const roles = await xml.getRoleInfo(appId);
189
- for (const role of roles.roles) {
190
- console.log(role.name, role.access.description);
191
- }
192
- ```
262
+ ## Other options
193
263
 
194
- Every method: [guides/xml-api.md](guides/xml-api.md).
264
+ - **`readOnly: true`** refuses every write before it is sent, in both APIs. Reads sent as POST (`runQuery`, `runReport`, `audit`) still work.
265
+ - **Dates**: ISO date strings in responses become `Date` objects. The types say `string`, because QuickBase's spec marks few date fields; set `convertDates: false` to keep strings.
266
+ - **Requirements**: Node.js 18 or later, or any modern browser. The package is ES modules; the browser bundle also defines a `QuickBase` global.
195
267
 
196
- ## Guides
268
+ ## Documentation
197
269
 
198
- - [Queries, pagination and records](guides/queries.md)
199
- - [Schema aliases](guides/schema-aliases.md)
200
- - [Authentication](guides/authentication.md)
201
- - [Rate limits and retries](guides/reliability.md)
202
- - [The XML API](guides/xml-api.md)
203
- - [Migrating from v1](guides/migration.md)
204
- - [API reference](https://drewbradfordxyz.github.io/quickbase-js/) and [CHANGELOG](CHANGELOG.md)
270
+ - Guides: [queries and pagination](guides/queries.md), [schema aliases](guides/schema-aliases.md), [authentication](guides/authentication.md), [rate limits and retries](guides/reliability.md), [the XML API](guides/xml-api.md), [migrating from v1](guides/migration.md)
271
+ - [API reference](https://drewbradfordxyz.github.io/quickbase-js/): every method, with QuickBase's descriptions
272
+ - [CHANGELOG](CHANGELOG.md)
205
273
 
206
274
  ## Development
207
275
 
@@ -213,7 +281,7 @@ npm run typecheck # the library, and the docs' examples
213
281
  npm run spec:build # regenerate the types from the pinned spec in spec/
214
282
  ```
215
283
 
216
- `spec/` is a submodule of [quickbase-spec](https://github.com/DrewBradfordXYZ/quickbase-spec). CI requires the generated types to match it. The code in this README and `guides/` comes from `examples/docs.ts`, which `npm run typecheck` checks (aliased examples there carry the casts TypeScript needs).
284
+ `spec/` is a submodule of [quickbase-spec](https://github.com/DrewBradfordXYZ/quickbase-spec), and CI fails when the generated types don't match it. The code in this README and `guides/` comes from `examples/docs.ts`, which `npm run typecheck` checks; change them together.
217
285
 
218
286
  ## Related
219
287