@23blocks/jsonapi-codec 3.0.0 → 3.0.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 (2) hide show
  1. package/README.md +232 -0
  2. package/package.json +1 -1
package/README.md ADDED
@@ -0,0 +1,232 @@
1
+ # @23blocks/jsonapi-codec
2
+
3
+ JSON:API v1.0 encoder/decoder for the 23blocks SDK.
4
+
5
+ [![npm version](https://img.shields.io/npm/v/@23blocks/jsonapi-codec.svg)](https://www.npmjs.com/package/@23blocks/jsonapi-codec)
6
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
7
+
8
+ ## Installation
9
+
10
+ ```bash
11
+ npm install @23blocks/jsonapi-codec
12
+ ```
13
+
14
+ ## Overview
15
+
16
+ This package provides utilities for encoding and decoding [JSON:API v1.0](https://jsonapi.org/) documents. It handles:
17
+
18
+ - **Type definitions** - Complete JSON:API type system
19
+ - **Decoding** - Parse single resources, collections, and paginated results
20
+ - **Relationship resolution** - Resolve included relationships
21
+ - **Error handling** - Convert JSON:API errors to SDK errors
22
+
23
+ ## Usage
24
+
25
+ ### Decoding Resources
26
+
27
+ ```typescript
28
+ import { decodeOne, decodeMany, decodePageResult } from '@23blocks/jsonapi-codec';
29
+ import type { JsonApiDocument } from '@23blocks/jsonapi-codec';
30
+
31
+ // Define a mapper for your resource type
32
+ const userMapper = (resource, included) => ({
33
+ id: resource.id,
34
+ email: resource.attributes?.email,
35
+ firstName: resource.attributes?.first_name,
36
+ lastName: resource.attributes?.last_name,
37
+ });
38
+
39
+ // Decode a single resource
40
+ const response: JsonApiDocument = await fetch('/api/users/123').then(r => r.json());
41
+ const user = decodeOne(response, userMapper);
42
+
43
+ // Decode a collection
44
+ const listResponse: JsonApiDocument = await fetch('/api/users').then(r => r.json());
45
+ const users = decodeMany(listResponse, userMapper);
46
+
47
+ // Decode paginated results
48
+ const pageResult = decodePageResult(listResponse, userMapper);
49
+ console.log(pageResult.data); // User[]
50
+ console.log(pageResult.meta.total); // Total count
51
+ ```
52
+
53
+ ### Working with Relationships
54
+
55
+ ```typescript
56
+ import {
57
+ buildIncludedMap,
58
+ resolveRelationship,
59
+ resolveRelationshipMany,
60
+ } from '@23blocks/jsonapi-codec';
61
+
62
+ const response: JsonApiDocument = await fetch('/api/users/123?include=roles,company').then(r => r.json());
63
+ const included = buildIncludedMap(response.included || []);
64
+
65
+ // Resolve single relationship
66
+ const company = resolveRelationship(
67
+ resource.relationships?.company,
68
+ included,
69
+ companyMapper
70
+ );
71
+
72
+ // Resolve many relationships
73
+ const roles = resolveRelationshipMany(
74
+ resource.relationships?.roles,
75
+ included,
76
+ roleMapper
77
+ );
78
+ ```
79
+
80
+ ### Error Handling
81
+
82
+ ```typescript
83
+ import {
84
+ throwIfError,
85
+ jsonApiErrorsToBlockError,
86
+ blockErrorFromJsonApi,
87
+ } from '@23blocks/jsonapi-codec';
88
+
89
+ // Automatically throw if response contains errors
90
+ const response = await fetch('/api/users/123').then(r => r.json());
91
+ throwIfError(response); // Throws BlockErrorException if errors present
92
+
93
+ // Convert JSON:API errors to BlockError
94
+ try {
95
+ const data = await apiCall();
96
+ } catch (err) {
97
+ const blockError = blockErrorFromJsonApi(err);
98
+ console.log(blockError.code, blockError.message);
99
+ }
100
+ ```
101
+
102
+ ### Type Guards
103
+
104
+ ```typescript
105
+ import {
106
+ isSingleResourceDocument,
107
+ isCollectionDocument,
108
+ isErrorDocument,
109
+ } from '@23blocks/jsonapi-codec';
110
+
111
+ const response = await fetch('/api/resource').then(r => r.json());
112
+
113
+ if (isErrorDocument(response)) {
114
+ // Handle errors
115
+ } else if (isSingleResourceDocument(response)) {
116
+ // Handle single resource
117
+ } else if (isCollectionDocument(response)) {
118
+ // Handle collection
119
+ }
120
+ ```
121
+
122
+ ### Creating Custom Mappers
123
+
124
+ ```typescript
125
+ import type { ResourceMapper, IncludedMap } from '@23blocks/jsonapi-codec';
126
+ import type { JsonApiResource } from '@23blocks/jsonapi-codec';
127
+
128
+ interface Product {
129
+ id: string;
130
+ name: string;
131
+ price: number;
132
+ category?: Category;
133
+ }
134
+
135
+ const productMapper: ResourceMapper<Product> = (
136
+ resource: JsonApiResource,
137
+ included: IncludedMap
138
+ ): Product => ({
139
+ id: resource.id,
140
+ name: resource.attributes?.name ?? '',
141
+ price: resource.attributes?.price ?? 0,
142
+ category: resolveRelationship(
143
+ resource.relationships?.category,
144
+ included,
145
+ categoryMapper
146
+ ),
147
+ });
148
+ ```
149
+
150
+ ## API Reference
151
+
152
+ ### Type Definitions
153
+
154
+ | Export | Type | Description |
155
+ |--------|------|-------------|
156
+ | `JsonApiDocument` | interface | Root JSON:API document |
157
+ | `JsonApiResource` | interface | Resource object |
158
+ | `JsonApiRelationship` | interface | Relationship object |
159
+ | `JsonApiResourceIdentifier` | interface | Resource identifier (type + id) |
160
+ | `JsonApiLinks` | interface | Links object |
161
+ | `JsonApiLink` | type | Link (string or object) |
162
+ | `JsonApiError` | interface | Error object |
163
+ | `JsonApiMeta` | interface | Meta object |
164
+
165
+ ### Type Guards
166
+
167
+ | Export | Description |
168
+ |--------|-------------|
169
+ | `isSingleResourceDocument(doc)` | Check if document has single resource |
170
+ | `isCollectionDocument(doc)` | Check if document has resource array |
171
+ | `isErrorDocument(doc)` | Check if document has errors |
172
+
173
+ ### Decoding Functions
174
+
175
+ | Export | Description |
176
+ |--------|-------------|
177
+ | `decodeOne(doc, mapper)` | Decode single resource (throws if not found) |
178
+ | `decodeOneOrNull(doc, mapper)` | Decode single resource (returns null if not found) |
179
+ | `decodeMany(doc, mapper)` | Decode resource collection |
180
+ | `decodePageResult(doc, mapper)` | Decode paginated collection |
181
+ | `extractPageMeta(doc)` | Extract pagination metadata |
182
+ | `decodeWith(mapper)` | Create decoder function with mapper |
183
+
184
+ ### Relationship Resolution
185
+
186
+ | Export | Description |
187
+ |--------|-------------|
188
+ | `buildIncludedMap(included)` | Build lookup map from included resources |
189
+ | `getIncluded(map, type, id)` | Get included resource by type and id |
190
+ | `resolveRelationship(rel, map, mapper)` | Resolve single relationship |
191
+ | `resolveRelationshipMany(rel, map, mapper)` | Resolve array relationship |
192
+ | `resourceKey(type, id)` | Create lookup key |
193
+
194
+ ### Error Handling
195
+
196
+ | Export | Description |
197
+ |--------|-------------|
198
+ | `jsonApiErrorToBlockError(error)` | Convert single JSON:API error |
199
+ | `jsonApiErrorsToBlockError(errors)` | Convert multiple JSON:API errors |
200
+ | `blockErrorFromJsonApi(doc)` | Extract error from document |
201
+ | `throwIfError(doc)` | Throw if document contains errors |
202
+ | `assertNotError(doc)` | Assert document is not an error |
203
+
204
+ ### Types
205
+
206
+ | Export | Description |
207
+ |--------|-------------|
208
+ | `ResourceMapper<T>` | Function type for mapping resources |
209
+ | `IncludedMap` | Map type for included resources |
210
+
211
+ ## TypeScript Support
212
+
213
+ This package is written in TypeScript and provides full type definitions:
214
+
215
+ ```typescript
216
+ import type {
217
+ JsonApiDocument,
218
+ JsonApiResource,
219
+ ResourceMapper,
220
+ IncludedMap,
221
+ } from '@23blocks/jsonapi-codec';
222
+ ```
223
+
224
+ ## Related Packages
225
+
226
+ - [`@23blocks/contracts`](https://www.npmjs.com/package/@23blocks/contracts) - Core types and interfaces
227
+ - [`@23blocks/transport-http`](https://www.npmjs.com/package/@23blocks/transport-http) - HTTP transport
228
+ - [`@23blocks/sdk`](https://www.npmjs.com/package/@23blocks/sdk) - Full SDK package
229
+
230
+ ## License
231
+
232
+ MIT - Copyright (c) 2024 23blocks
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@23blocks/jsonapi-codec",
3
- "version": "3.0.0",
3
+ "version": "3.0.1",
4
4
  "description": "JSON:API v1.0 codec for encoding/decoding API responses",
5
5
  "license": "MIT",
6
6
  "author": "23blocks <hello@23blocks.com>",