@jphil/bookwhen-client 0.4.2 → 0.5.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 +67 -300
- package/dist/index.d.ts +350 -0
- package/dist/index.js +236 -0
- package/dist/index.js.map +1 -1
- package/package.json +20 -19
package/README.md
CHANGED
|
@@ -1,351 +1,118 @@
|
|
|
1
1
|
# `@jphil/bookwhen-client`
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
TypeScript client for the [Bookwhen API v2](https://api.bookwhen.com/v2), built for Node.js and browser environments.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
This package is currently pre-1.0 and under active development.
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
- [Features](#features)
|
|
9
|
-
- [Installation](#installation)
|
|
10
|
-
- [Usage](#usage)
|
|
11
|
-
- [JSON:API Response Structure](#jsonapi-response-structure)
|
|
12
|
-
- [Migration from v0.3.2](#migration-from-v032)
|
|
13
|
-
- [Browser Usage](#browser-usage)
|
|
14
|
-
- [Error Handling](#error-handling)
|
|
15
|
-
- [Configuration](#configuration)
|
|
16
|
-
- [Contributing](#contributing)
|
|
17
|
-
- [Roadmap](#roadmap)
|
|
18
|
-
- [License](#license)
|
|
7
|
+
## Quick Start
|
|
19
8
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
You'll likely be at least somewhat familiar with the [Bookwhen](https://www.bookwhen.com) booking platform if you've landed here. But if not, you'll want to have a look at their [API (v2) documentation](https://api.bookwhen.com/v2). There's also a nice [Swagger style layout of the Bookwhen API v2 docs](https://petstore.swagger.io/?url=https://api.bookwhen.com/v2/openapi.yaml)
|
|
23
|
-
|
|
24
|
-
## Features
|
|
25
|
-
|
|
26
|
-
- Provides an easy way to access Bookwhen API from both NodeJS and browsers
|
|
27
|
-
- Browser-compatible with proper CORS handling
|
|
28
|
-
- Provides fully typed methods for each model (so far just the Events model) provided in the Bookwhen API v2
|
|
29
|
-
- **Full JSON:API compliance** with access to included resources, links, and meta data
|
|
30
|
-
- **Relationship resolution utilities** for working with included data
|
|
31
|
-
- **Type-safe JSON:API response interfaces** with proper TypeScript support
|
|
32
|
-
|
|
33
|
-
## Installation
|
|
34
|
-
|
|
35
|
-
Install via pnpm:
|
|
9
|
+
Install the library and peer dependencies:
|
|
36
10
|
|
|
37
11
|
```bash
|
|
38
|
-
pnpm add @jphil/bookwhen-client
|
|
12
|
+
pnpm add @jphil/bookwhen-client axios zod
|
|
39
13
|
```
|
|
40
14
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
```bash
|
|
44
|
-
pnpm add axios zod
|
|
45
|
-
# or npm install axios zod
|
|
46
|
-
# or yarn add axios zod
|
|
47
|
-
```
|
|
48
|
-
|
|
49
|
-
## Usage
|
|
50
|
-
|
|
51
|
-
This library is distributed as an ES module. The following usage pattern applies to modern Node.js environments (with `type: "module"` in your `package.json` or when using `.mjs` files) and browser environments when using a bundler. For direct browser usage without a bundler, see the [Browser Usage](#browser-usage) section below.
|
|
15
|
+
Create a client and fetch events:
|
|
52
16
|
|
|
53
17
|
```typescript
|
|
54
|
-
// import the client factory
|
|
55
18
|
import { createBookwhenClient } from '@jphil/bookwhen-client';
|
|
56
19
|
|
|
57
|
-
// create the client
|
|
58
20
|
const client = createBookwhenClient({
|
|
59
|
-
apiKey:
|
|
60
|
-
debug: true, // Optional: enables request logging
|
|
21
|
+
apiKey: process.env.BOOKWHEN_API_KEY!,
|
|
61
22
|
});
|
|
62
23
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
includes: ['location', 'tickets'], // Optional: include related resources
|
|
67
|
-
});
|
|
68
|
-
const event = eventResponse.data; // Access the event data
|
|
69
|
-
|
|
70
|
-
// get all events - returns full JSON:API response
|
|
71
|
-
const eventsResponse = await client.events.getMultiple();
|
|
72
|
-
const events = eventsResponse.data; // Access the events array
|
|
73
|
-
|
|
74
|
-
// get all events in 2025 tagged with 'workshop' with included locations
|
|
75
|
-
const events2025Response = await client.events.getMultiple({
|
|
76
|
-
filters: {
|
|
77
|
-
// Optional: filter by various
|
|
78
|
-
from: '20250101',
|
|
79
|
-
to: '20251231',
|
|
80
|
-
tag: ['workshop'],
|
|
81
|
-
},
|
|
82
|
-
includes: ['location'], // Optional: Include related resources
|
|
24
|
+
const response = await client.events.getMultiple({
|
|
25
|
+
filters: { from: '20250101', to: '20251231' },
|
|
26
|
+
includes: ['location', 'tickets'],
|
|
83
27
|
});
|
|
84
|
-
const events2025 = events2025Response.data; // Access the events array
|
|
85
|
-
const includedLocations = events2025Response.included; // Access included location data
|
|
86
|
-
```
|
|
87
|
-
|
|
88
|
-
(N.B. Ensure you wrap the above statements in try/catch blocks to catch errors which could be thrown)
|
|
89
28
|
|
|
90
|
-
|
|
29
|
+
const events = response.data;
|
|
30
|
+
const included = response.included;
|
|
31
|
+
```
|
|
91
32
|
|
|
92
|
-
##
|
|
33
|
+
## API Coverage
|
|
93
34
|
|
|
94
|
-
|
|
35
|
+
Bookwhen resources currently implemented in this client:
|
|
95
36
|
|
|
96
|
-
|
|
37
|
+
| Resource | Status | Endpoints |
|
|
38
|
+
| ------------ | ----------- | ------------------------------------------------ |
|
|
39
|
+
| Events | Implemented | `/events`, `/events/{event_id}` |
|
|
40
|
+
| Tickets | Implemented | `/tickets`, `/tickets/{ticket_id}` |
|
|
41
|
+
| Locations | Implemented | `/locations`, `/locations/{location_id}` |
|
|
42
|
+
| Attachments | Implemented | `/attachments`, `/attachments/{attachment_id}` |
|
|
43
|
+
| Class passes | Implemented | `/class_passes`, `/class_passes/{class_pass_id}` |
|
|
97
44
|
|
|
98
|
-
|
|
99
|
-
const response = await client.events.getMultiple({
|
|
100
|
-
includes: ['location', 'tickets']
|
|
101
|
-
});
|
|
45
|
+
Reference docs:
|
|
102
46
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
data: [
|
|
106
|
-
{
|
|
107
|
-
id: 'ev-123',
|
|
108
|
-
type: 'event',
|
|
109
|
-
attributes: {
|
|
110
|
-
title: 'Thai massage with Justin',
|
|
111
|
-
start_at: '2025-11-02T11:00:00.000+00:00',
|
|
112
|
-
// ... other event attributes
|
|
113
|
-
},
|
|
114
|
-
relationships: {
|
|
115
|
-
location: { data: { id: 'loc-1', type: 'location' } },
|
|
116
|
-
tickets: { data: [{ id: 'ticket-1', type: 'ticket' }] }
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
],
|
|
120
|
-
included: [
|
|
121
|
-
{
|
|
122
|
-
id: 'loc-1',
|
|
123
|
-
type: 'location',
|
|
124
|
-
attributes: {
|
|
125
|
-
address_text: 'MovingStillness Studio\nColdean\nBrighton',
|
|
126
|
-
latitude: 50.8608545,
|
|
127
|
-
longitude: -0.1070177
|
|
128
|
-
}
|
|
129
|
-
},
|
|
130
|
-
{
|
|
131
|
-
id: 'ticket-1',
|
|
132
|
-
type: 'ticket',
|
|
133
|
-
attributes: {
|
|
134
|
-
name: 'Standard Ticket',
|
|
135
|
-
price: 100,
|
|
136
|
-
available: true
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
],
|
|
140
|
-
links: {
|
|
141
|
-
self: 'https://api.bookwhen.com/v2/events'
|
|
142
|
-
},
|
|
143
|
-
meta: {
|
|
144
|
-
// Optional metadata
|
|
145
|
-
}
|
|
146
|
-
}
|
|
147
|
-
```
|
|
47
|
+
- Bookwhen API docs: https://api.bookwhen.com/v2
|
|
48
|
+
- Swagger layout: https://petstore.swagger.io/?url=https://api.bookwhen.com/v2/openapi.yaml
|
|
148
49
|
|
|
149
|
-
|
|
50
|
+
## Response Shape
|
|
150
51
|
|
|
151
|
-
|
|
52
|
+
Service methods return full JSON:API response envelopes (not just `data`).
|
|
152
53
|
|
|
153
54
|
```typescript
|
|
154
|
-
import { resolveJsonApiRelationships, resolveJsonApiResource } from '@jphil/bookwhen-client';
|
|
155
|
-
|
|
156
|
-
// For multiple events
|
|
157
|
-
const eventsResponse = await client.events.getMultiple({
|
|
158
|
-
includes: ['location', 'tickets']
|
|
159
|
-
});
|
|
160
|
-
|
|
161
|
-
// Resolve relationships for all events
|
|
162
|
-
const resolvedEvents = resolveJsonApiRelationships(
|
|
163
|
-
eventsResponse.data,
|
|
164
|
-
eventsResponse.included
|
|
165
|
-
);
|
|
166
|
-
|
|
167
|
-
// For a single event
|
|
168
55
|
const eventResponse = await client.events.getById({
|
|
169
56
|
eventId: 'ev-123',
|
|
170
|
-
includes: ['location']
|
|
57
|
+
includes: ['location'],
|
|
171
58
|
});
|
|
172
59
|
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
);
|
|
60
|
+
eventResponse.data;
|
|
61
|
+
eventResponse.included;
|
|
62
|
+
eventResponse.links;
|
|
63
|
+
eventResponse.meta;
|
|
178
64
|
```
|
|
179
65
|
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
Version 0.4.0 introduces breaking changes to return full JSON:API responses:
|
|
183
|
-
|
|
184
|
-
### Before (v0.3.2)
|
|
185
|
-
```typescript
|
|
186
|
-
const events = await client.events.getMultiple(); // BookwhenEvent[]
|
|
187
|
-
const event = await client.events.getById({ eventId: '123' }); // BookwhenEvent
|
|
188
|
-
```
|
|
189
|
-
|
|
190
|
-
### After (v0.4.0)
|
|
191
|
-
```typescript
|
|
192
|
-
const response = await client.events.getMultiple(); // EventsResponse
|
|
193
|
-
const events = response.data; // BookwhenEvent[]
|
|
194
|
-
|
|
195
|
-
const eventResponse = await client.events.getById({ eventId: '123' }); // EventResponse
|
|
196
|
-
const event = eventResponse.data; // BookwhenEvent
|
|
197
|
-
```
|
|
198
|
-
|
|
199
|
-
Services for the other models in the API are in the pipeline.
|
|
200
|
-
|
|
201
|
-
N.B. This library is still a pre-1.0.0 WIP, please use accordingly, and pls submit issues for any bugs!
|
|
202
|
-
|
|
203
|
-
## Browser Usage
|
|
204
|
-
|
|
205
|
-
The client is well-suited for browser environments.
|
|
206
|
-
|
|
207
|
-
### With a Bundler (Recommended for Browser Projects)
|
|
208
|
-
|
|
209
|
-
If you are using a JavaScript bundler (like Webpack, Rollup, Vite, Parcel, etc.) in your browser project, you can import and use the client as shown in the main [Usage](#usage) section:
|
|
66
|
+
Relationship helper utilities are exported:
|
|
210
67
|
|
|
211
68
|
```typescript
|
|
212
|
-
import {
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
Example import map:
|
|
226
|
-
|
|
227
|
-
```html
|
|
228
|
-
<script type="importmap">
|
|
229
|
-
{
|
|
230
|
-
"imports": {
|
|
231
|
-
"@jphil/bookwhen-client": "/node_modules/@jphil/bookwhen-client/dist/index.es.js",
|
|
232
|
-
"axios": "/node_modules/axios/dist/esm/axios.js",
|
|
233
|
-
"zod": "/node_modules/zod/lib/index.mjs"
|
|
234
|
-
}
|
|
235
|
-
}
|
|
236
|
-
</script>
|
|
237
|
-
<script type="module">
|
|
238
|
-
import { createBookwhenClient } from '@jphil/bookwhen-client';
|
|
239
|
-
// ...
|
|
240
|
-
</script>
|
|
69
|
+
import {
|
|
70
|
+
resolveJsonApiRelationships,
|
|
71
|
+
resolveJsonApiResource,
|
|
72
|
+
} from '@jphil/bookwhen-client';
|
|
73
|
+
|
|
74
|
+
const resolvedMany = resolveJsonApiRelationships(
|
|
75
|
+
response.data,
|
|
76
|
+
response.included,
|
|
77
|
+
);
|
|
78
|
+
const resolvedOne = resolveJsonApiResource(
|
|
79
|
+
eventResponse.data,
|
|
80
|
+
eventResponse.included,
|
|
81
|
+
);
|
|
241
82
|
```
|
|
242
83
|
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
### Important Note on API Keys in Client-Side Usage
|
|
246
|
-
|
|
247
|
-
When using this library in a browser (client-side), your Bookwhen API key will necessarily be included in the client-side code and thus visible.
|
|
84
|
+
## Browser Notes
|
|
248
85
|
|
|
249
|
-
-
|
|
250
|
-
-
|
|
86
|
+
- The library works in browser contexts and includes browser-aware error handling.
|
|
87
|
+
- Client-side API keys are visible to users; use least-privilege Bookwhen API tokens.
|
|
251
88
|
|
|
252
|
-
##
|
|
89
|
+
## Development
|
|
253
90
|
|
|
254
|
-
|
|
91
|
+
Common local commands:
|
|
255
92
|
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
### Error Types
|
|
263
|
-
|
|
264
|
-
- `NETWORK_ERROR`: Indicates a failure in API communication (e.g., DNS resolution, connection refused).
|
|
265
|
-
- `SECURITY_ERROR`: Specific to browsers, indicates security restrictions prevented API access (e.g., CORS issues not handled by the server, mixed content).
|
|
266
|
-
- `API_ERROR`: The Bookwhen API returned an error response (e.g., 4xx or 5xx status code). The `context` may include `statusCode` and `responseData`.
|
|
267
|
-
- `CONFIG_ERROR`: The client was configured incorrectly (e.g., missing API key).
|
|
268
|
-
- `UNKNOWN_ERROR`: An unexpected error occurred within the library.
|
|
269
|
-
|
|
270
|
-
Example:
|
|
271
|
-
|
|
272
|
-
```typescript
|
|
273
|
-
try {
|
|
274
|
-
await client.events.getById({ eventId: 'invalid-id' });
|
|
275
|
-
} catch (error: any) {
|
|
276
|
-
// It's good practice to type the error if you have custom error types defined
|
|
277
|
-
console.error(`Error Code: ${error.code}`);
|
|
278
|
-
console.error(`Message: ${error.message}`);
|
|
279
|
-
if (error.code === 'API_ERROR') {
|
|
280
|
-
console.error('API Error Details:', error.context?.responseData);
|
|
281
|
-
} else if (error.code === 'NETWORK_ERROR') {
|
|
282
|
-
// Handle network issues, maybe retry or inform user
|
|
283
|
-
}
|
|
284
|
-
// Other error handling...
|
|
285
|
-
}
|
|
93
|
+
```bash
|
|
94
|
+
pnpm install
|
|
95
|
+
pnpm test
|
|
96
|
+
pnpm build
|
|
97
|
+
pnpm lint
|
|
286
98
|
```
|
|
287
99
|
|
|
288
|
-
##
|
|
289
|
-
|
|
290
|
-
Required configuration:
|
|
291
|
-
|
|
292
|
-
- **apiKey**: Your Bookwhen API key (required)
|
|
293
|
-
|
|
294
|
-
API requests to the Bookwhen API are authenticated using Basic Authentication with the API Key as the username and a blank password.
|
|
295
|
-
|
|
296
|
-
API keys can be generated in the [API tokens setup area of your Bookwhen account](https://admin.bookwhen.com/settings/api_access_permission_sets). (This will link to the API settings page in your Bookwhen account if you have one and are logged into your admin account)
|
|
297
|
-
|
|
298
|
-
## Contributing
|
|
299
|
-
|
|
300
|
-
Please see the docs in the CONTRIBUTIONS.md file, thanks!
|
|
301
|
-
|
|
302
|
-
## Mainainter release process
|
|
303
|
-
|
|
304
|
-
[refining]
|
|
305
|
-
|
|
306
|
-
From main branch on local:
|
|
100
|
+
## Documentation
|
|
307
101
|
|
|
308
|
-
|
|
309
|
-
- git checkout -b some-new-branch
|
|
310
|
-
- git commit -m 'feat(context): my latest work on feature x'
|
|
311
|
-
- git push, copy URL
|
|
102
|
+
Project docs are intentionally lightweight and living:
|
|
312
103
|
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
-
|
|
316
|
-
-
|
|
317
|
-
|
|
318
|
-
On local:
|
|
319
|
-
|
|
320
|
-
- checkout main
|
|
321
|
-
- git pull
|
|
322
|
-
- git branch -d release (so we have a clean release branch)
|
|
323
|
-
- git checkout -b release
|
|
324
|
-
- pnpm changeset (provide changelog message - commit will occur in next step, not this one)
|
|
325
|
-
- pnpm changeset version (bumps version numbers, and updates changelog, and commits >>> note new version number)
|
|
326
|
-
- git push
|
|
327
|
-
|
|
328
|
-
On github:
|
|
329
|
-
|
|
330
|
-
- Open PR for release to merge into main
|
|
331
|
-
- Perfect the PR, merge when checks pass (check why no build)
|
|
332
|
-
|
|
333
|
-
On local:
|
|
334
|
-
|
|
335
|
-
- git checkout main
|
|
336
|
-
- git pull
|
|
337
|
-
- git tag -a vx.x.x -m 'release vx.x.x'
|
|
338
|
-
- git push origin vx.x.x <<<< RELEASE to github and NPM
|
|
104
|
+
- [CONTRIBUTING.md](CONTRIBUTING.md) - Contribution workflow and release notes
|
|
105
|
+
- [TODO.md](TODO.md) - Active work and backlog
|
|
106
|
+
- [DECISIONS.md](DECISIONS.md) - Architectural decisions
|
|
107
|
+
- [LEARNINGS.md](LEARNINGS.md) - Validated findings and gotchas
|
|
108
|
+
- [AGENTS.md](AGENTS.md) - Context and constraints for coding agents
|
|
339
109
|
|
|
340
110
|
## Roadmap
|
|
341
111
|
|
|
342
|
-
-
|
|
343
|
-
-
|
|
344
|
-
|
|
345
|
-
### Todos
|
|
346
|
-
|
|
347
|
-
@see the issue queue.
|
|
112
|
+
- Expand coverage to remaining Bookwhen v2 content models.
|
|
113
|
+
- Continue improving test depth across browser and Node environments.
|
|
114
|
+
- Iterate toward a stable 1.0 release with community feedback.
|
|
348
115
|
|
|
349
116
|
## License
|
|
350
117
|
|
|
351
|
-
ISC License. See [LICENSE](LICENSE)
|
|
118
|
+
ISC License. See [LICENSE](LICENSE).
|