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.
- package/README.md +144 -76
- package/dist/quickbase.esm.js +3 -3
- package/dist/quickbase.min.js +3 -3
- package/dist/quickbase.min.js.map +1 -1
- package/package.json +13 -9
- package/src/auth/index.ts +55 -0
- package/src/auth/sso-token.ts +143 -0
- package/src/auth/temp-token.ts +160 -0
- package/src/auth/ticket.ts +329 -0
- package/src/auth/token-cache.ts +97 -0
- package/src/auth/types.ts +50 -0
- package/src/auth/user-token.ts +38 -0
- package/src/cli/schema.ts +512 -0
- package/src/client/index.ts +36 -0
- package/src/client/pagination.ts +337 -0
- package/src/client/request.ts +544 -0
- package/src/client/retry.ts +169 -0
- package/src/client/throttle.ts +105 -0
- package/src/core/config.ts +160 -0
- package/src/core/dates.ts +63 -0
- package/src/core/errors.ts +349 -0
- package/src/core/index.ts +50 -0
- package/src/core/logger.ts +113 -0
- package/src/core/schema.ts +213 -0
- package/src/core/transform.ts +295 -0
- package/src/core/types.ts +337 -0
- package/src/generated/client.ts +1785 -0
- package/src/generated/operations.json +672 -0
- package/src/generated/types.ts +4884 -0
- package/src/index.ts +259 -0
- package/src/xml/codepages.ts +119 -0
- package/src/xml/dbvars.ts +75 -0
- package/src/xml/errors.ts +133 -0
- package/src/xml/fields.ts +121 -0
- package/src/xml/groups.ts +560 -0
- package/src/xml/html.ts +152 -0
- package/src/xml/index.ts +1196 -0
- package/src/xml/metadata.ts +111 -0
- package/src/xml/records.ts +301 -0
- package/src/xml/request.ts +238 -0
- package/src/xml/roles.ts +321 -0
- package/src/xml/schema.ts +436 -0
- package/src/xml/transform.ts +381 -0
- package/src/xml/types.ts +602 -0
- package/src/xml/users.ts +221 -0
- package/src/xml/webhooks.ts +260 -0
package/README.md
CHANGED
|
@@ -1,14 +1,22 @@
|
|
|
1
|
-
#
|
|
1
|
+
# quickbase-js
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
[](https://www.npmjs.com/package/quickbase-js)
|
|
4
|
+
[](https://github.com/DrewBradfordXYZ/quickbase-js/actions/workflows/ci.yml)
|
|
5
|
+
[](LICENSE)
|
|
4
6
|
|
|
5
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
##
|
|
74
|
+
## Common tasks
|
|
63
75
|
|
|
64
|
-
|
|
76
|
+
### Get every record from a table
|
|
65
77
|
|
|
66
78
|
```typescript
|
|
67
|
-
await qb.
|
|
68
|
-
await qb.runQuery({ from: 'bqtable1'
|
|
69
|
-
await qb.
|
|
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
|
-
|
|
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
|
-
|
|
86
|
+
### Filter and sort
|
|
76
87
|
|
|
77
|
-
|
|
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
|
|
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
|
-
|
|
106
|
+
### Add or update records
|
|
86
107
|
|
|
87
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
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
|
-
|
|
185
|
-
import { createXmlClient } from 'quickbase-js';
|
|
260
|
+
The XML API adds `QuickbaseTicketError`, `QuickbaseAppTokenError` and `QuickbaseXmlError`.
|
|
186
261
|
|
|
187
|
-
|
|
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
|
-
|
|
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
|
-
##
|
|
268
|
+
## Documentation
|
|
197
269
|
|
|
198
|
-
- [
|
|
199
|
-
- [
|
|
200
|
-
- [
|
|
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)
|
|
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
|
|