perspectapi-ts-sdk 6.5.9 → 7.0.0
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 +46 -1011
- package/dist/chunk-K3T2AFYA.mjs +1393 -0
- package/dist/index-CWvUyMt3.d.mts +2224 -0
- package/dist/index-CWvUyMt3.d.ts +2224 -0
- package/dist/index.d.mts +130 -2221
- package/dist/index.d.ts +130 -2221
- package/dist/index.js +8 -2
- package/dist/index.mjs +13 -1364
- package/dist/v2/index.d.mts +1 -0
- package/dist/v2/index.d.ts +1 -0
- package/dist/v2/index.js +1419 -0
- package/dist/v2/index.mjs +40 -0
- package/docs/README.md +15 -0
- package/docs/v1-deprecated/README.md +9 -0
- package/docs/v1-deprecated/examples/README.md +324 -0
- package/docs/v1-deprecated/examples/basic-usage.ts +258 -0
- package/docs/v1-deprecated/examples/cloudflare-worker.ts +274 -0
- package/docs/v1-deprecated/examples/content-query-with-slug-prefix.ts +237 -0
- package/docs/v1-deprecated/examples/image-transforms.ts +200 -0
- package/docs/v1-deprecated/examples/site-user-checkout.ts +186 -0
- package/docs/v1-deprecated/examples/slug-prefix-examples.ts +491 -0
- package/docs/v1-deprecated/legacy-docs/caching.md +667 -0
- package/docs/v1-deprecated/legacy-docs/contact.md +1396 -0
- package/docs/v1-deprecated/legacy-docs/csrf-protection.md +664 -0
- package/docs/v1-deprecated/legacy-docs/image-transforms.md +523 -0
- package/docs/v1-deprecated/legacy-docs/loaders.md +304 -0
- package/docs/v1-deprecated/legacy-docs/newsletter.md +811 -0
- package/docs/v1-deprecated/legacy-docs/site-users.md +817 -0
- package/docs/v1-deprecated/legacy-notes/CHANGELOG-CHECKOUT.md +143 -0
- package/docs/v1-deprecated/legacy-notes/CSRF-CHECKOUT.md +271 -0
- package/docs/v1-deprecated/legacy-notes/IMAGE_TRANSFORMS_PORT.md +298 -0
- package/docs/v1-deprecated/sdk-readme.md +1076 -0
- package/examples/README.md +19 -0
- package/examples/basic-v2.ts +37 -0
- package/llms.txt +25 -0
- package/package.json +18 -7
- package/src/client/api-keys-client.ts +4 -0
- package/src/client/auth-client.ts +4 -0
- package/src/client/base-client.ts +7 -0
- package/src/client/bundles-client.ts +4 -0
- package/src/client/categories-client.ts +4 -0
- package/src/client/checkout-client.ts +4 -0
- package/src/client/contact-client.ts +4 -0
- package/src/client/content-client.ts +4 -0
- package/src/client/newsletter-client.ts +4 -0
- package/src/client/newsletter-management-client.ts +4 -0
- package/src/client/organizations-client.ts +4 -0
- package/src/client/products-client.ts +4 -0
- package/src/client/site-users-client.ts +10 -1
- package/src/client/sites-client.ts +4 -0
- package/src/client/webhooks-client.ts +4 -0
- package/src/deprecation.ts +2 -1
- package/src/index.ts +2 -1
- package/src/loaders.ts +59 -0
- package/src/perspect-api-client.ts +2 -2
- package/src/v2/client/orders-client.ts +6 -1
- package/src/v2/types.ts +3 -0
|
@@ -0,0 +1,304 @@
|
|
|
1
|
+
# High-Level Loader Helpers
|
|
2
|
+
|
|
3
|
+
> Deprecated v1 material. Do not copy these examples into new code. v1 sunsets
|
|
4
|
+
> on 2026-06-01; use `createPerspectApiV2Client` from
|
|
5
|
+
> `perspectapi-ts-sdk/v2` and `/api/v2`.
|
|
6
|
+
|
|
7
|
+
This guide dives into the higher-level helpers exported from `perspectapi-ts-sdk/src/loaders.ts`. They provide a batteries‑included experience for common content and commerce use cases while still working nicely with Remix, Next.js, Cloudflare Workers, or any node/edge runtime.
|
|
8
|
+
|
|
9
|
+
## Table of Contents
|
|
10
|
+
|
|
11
|
+
1. [Core Concepts](#core-concepts)
|
|
12
|
+
2. [Basic Usage](#basic-usage)
|
|
13
|
+
3. [Server Framework Examples](#server-framework-examples)
|
|
14
|
+
- [Remix/Express Route](#remixexpress-route)
|
|
15
|
+
- [Next.js App Route (Edge)](#nextjs-app-route-edge)
|
|
16
|
+
- [Cloudflare Worker](#cloudflare-worker)
|
|
17
|
+
4. [Fallback Data Strategies](#fallback-data-strategies)
|
|
18
|
+
5. [Custom Logging](#custom-logging)
|
|
19
|
+
6. [Creating Checkout Sessions](#creating-checkout-sessions)
|
|
20
|
+
7. [Type Reference](#type-reference)
|
|
21
|
+
|
|
22
|
+
---
|
|
23
|
+
|
|
24
|
+
## Core Concepts
|
|
25
|
+
|
|
26
|
+
- **Shared Options** – Every loader accepts a `LoaderOptions` object (`client`, `siteName`, optional `logger`, optional fallbacks, optional `limit`).
|
|
27
|
+
- **Normalised Types** – Returned objects use the SDK’s `Product`, `MediaItem`, and `BlogPost` types, which flatten nested media arrays and expose Stripe/Gateway identifiers when present.
|
|
28
|
+
- **Graceful Fallbacks** – When no PerspectAPI client is provided (or an API call fails), the helpers can emit sample data so your UI keeps rendering during local development.
|
|
29
|
+
- **Checkout Helper** – `createCheckoutSession` converts cart line items into Stripe price IDs (live/test aware) before hitting PerspectAPI’s checkout endpoint.
|
|
30
|
+
|
|
31
|
+
```typescript
|
|
32
|
+
import {
|
|
33
|
+
loadProducts,
|
|
34
|
+
loadProductBySlug,
|
|
35
|
+
loadPosts,
|
|
36
|
+
loadPages,
|
|
37
|
+
loadAllContent,
|
|
38
|
+
loadContentBySlug,
|
|
39
|
+
createCheckoutSession,
|
|
40
|
+
type LoaderOptions
|
|
41
|
+
} from 'perspectapi-ts-sdk';
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## Basic Usage
|
|
45
|
+
|
|
46
|
+
```typescript
|
|
47
|
+
import { createPerspectApiClient, loadProducts } from 'perspectapi-ts-sdk';
|
|
48
|
+
|
|
49
|
+
const client = createPerspectApiClient({
|
|
50
|
+
baseUrl: process.env.PERSPECTAPI_BASE_URL!,
|
|
51
|
+
apiKey: process.env.PERSPECTAPI_API_KEY!
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
const options: LoaderOptions = {
|
|
55
|
+
client,
|
|
56
|
+
siteName: 'museum-indian-art'
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
const products = await loadProducts(options);
|
|
60
|
+
console.log(products[0].product, products[0].media?.[0]);
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
## Server Framework Examples
|
|
64
|
+
|
|
65
|
+
### Remix/Express Route
|
|
66
|
+
|
|
67
|
+
```typescript
|
|
68
|
+
// app/routes/api/products.ts
|
|
69
|
+
import { json } from '@remix-run/node';
|
|
70
|
+
import {
|
|
71
|
+
loadProducts,
|
|
72
|
+
type LoaderOptions
|
|
73
|
+
} from 'perspectapi-ts-sdk';
|
|
74
|
+
|
|
75
|
+
export async function loader() {
|
|
76
|
+
const options: LoaderOptions = {
|
|
77
|
+
client: createPerspectApiClient({
|
|
78
|
+
baseUrl: process.env.PERSPECTAPI_BASE_URL!,
|
|
79
|
+
apiKey: process.env.PERSPECTAPI_API_KEY!
|
|
80
|
+
}),
|
|
81
|
+
siteName: process.env.PERSPECTAPI_SITE_NAME ?? 'museum-indian-art'
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
const products = await loadProducts(options);
|
|
85
|
+
return json(products);
|
|
86
|
+
}
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
### Next.js App Route (Edge)
|
|
90
|
+
|
|
91
|
+
```typescript
|
|
92
|
+
// app/api/products/route.ts
|
|
93
|
+
import {
|
|
94
|
+
loadProducts,
|
|
95
|
+
type LoaderOptions
|
|
96
|
+
} from 'perspectapi-ts-sdk';
|
|
97
|
+
|
|
98
|
+
export const runtime = 'edge';
|
|
99
|
+
|
|
100
|
+
export async function GET() {
|
|
101
|
+
const options: LoaderOptions = {
|
|
102
|
+
client: createPerspectApiClient({
|
|
103
|
+
baseUrl: process.env.PERSPECTAPI_BASE_URL!,
|
|
104
|
+
apiKey: process.env.PERSPECTAPI_API_KEY!
|
|
105
|
+
}),
|
|
106
|
+
siteName: process.env.PERSPECTAPI_SITE_NAME ?? 'museum-indian-art'
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
const products = await loadProducts(options);
|
|
110
|
+
return Response.json(products);
|
|
111
|
+
}
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
### Cloudflare Worker
|
|
115
|
+
|
|
116
|
+
```typescript
|
|
117
|
+
import {
|
|
118
|
+
createPerspectApiClient,
|
|
119
|
+
loadPosts
|
|
120
|
+
} from 'perspectapi-ts-sdk';
|
|
121
|
+
|
|
122
|
+
export default {
|
|
123
|
+
async fetch(_request: Request, env: Env) {
|
|
124
|
+
const client = createPerspectApiClient({
|
|
125
|
+
baseUrl: env.PERSPECTAPI_BASE_URL,
|
|
126
|
+
apiKey: env.PERSPECTAPI_API_KEY
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
const posts = await loadPosts({
|
|
130
|
+
client,
|
|
131
|
+
siteName: env.PERSPECTAPI_SITE_NAME ?? 'museum-indian-art',
|
|
132
|
+
logger: env.ENVIRONMENT === 'dev' ? console : undefined
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
return new Response(JSON.stringify(posts), {
|
|
136
|
+
headers: { 'content-type': 'application/json' }
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
};
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
## Fallback Data Strategies
|
|
143
|
+
|
|
144
|
+
If PerspectAPI credentials are missing (for example, in Storybook or local prototyping), omitting the `client` will trigger the built-in sample data:
|
|
145
|
+
|
|
146
|
+
```typescript
|
|
147
|
+
const products = await loadProducts({
|
|
148
|
+
client: null,
|
|
149
|
+
siteName: 'Demo Site'
|
|
150
|
+
});
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
You can also provide your own mock datasets:
|
|
154
|
+
|
|
155
|
+
```typescript
|
|
156
|
+
const fallbackProducts = [
|
|
157
|
+
{
|
|
158
|
+
id: 'demo-1',
|
|
159
|
+
product: 'Placeholder Canvas',
|
|
160
|
+
price: 199,
|
|
161
|
+
slug: 'placeholder-canvas',
|
|
162
|
+
media: [{ media_id: 'm-1', link: 'https://picsum.photos/800/600', attachment_id: 1 }]
|
|
163
|
+
}
|
|
164
|
+
];
|
|
165
|
+
|
|
166
|
+
const products = await loadProducts({
|
|
167
|
+
client: null,
|
|
168
|
+
siteName: 'Demo Site',
|
|
169
|
+
fallbackProducts
|
|
170
|
+
});
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
## Custom Logging
|
|
174
|
+
|
|
175
|
+
All loaders accept an optional `logger` implementing `LoaderLogger`. Supplying `console` is enough during development:
|
|
176
|
+
|
|
177
|
+
```typescript
|
|
178
|
+
const options = {
|
|
179
|
+
client,
|
|
180
|
+
siteName: 'museum-indian-art',
|
|
181
|
+
logger: {
|
|
182
|
+
info: (...args) => myLogger.info('perspectapi', ...args),
|
|
183
|
+
warn: (...args) => myLogger.warn('perspectapi', ...args),
|
|
184
|
+
error: (...args) => myLogger.error('perspectapi', ...args)
|
|
185
|
+
}
|
|
186
|
+
};
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
Every helper emits consistent messages (e.g. `[PerspectAPI] Loading products…`, `[PerspectAPI] Error loading posts`) that flow through your custom logger.
|
|
190
|
+
|
|
191
|
+
## Filtering Products
|
|
192
|
+
|
|
193
|
+
`loadProducts` accepts optional `search`, `category`, `categoryIds`, and `offset` fields. `category` can be a single display name, a comma-separated string, or an array of names. `categoryIds` accepts one or many numeric/string IDs. PerspectAPI resolves every supplied value before filtering, so callers never need to hard-code IDs. Use `offset` when you want manual pagination instead of the default page-based flow.
|
|
194
|
+
|
|
195
|
+
```typescript
|
|
196
|
+
const decorPillows = await loadProducts({
|
|
197
|
+
...options,
|
|
198
|
+
category: ['Home Decor', 'Kitchen Essentials'],
|
|
199
|
+
categoryIds: [12, '18'],
|
|
200
|
+
offset: 24,
|
|
201
|
+
search: 'pillow',
|
|
202
|
+
limit: 12
|
|
203
|
+
});
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
You can mix categories by name and ID in the same call; the API returns any product that belongs to at least one of the requested categories. If none of the provided names resolve (and no IDs are supplied), the API responds with `success: true` and an empty data array, giving you a clean signal to fall back or show alternative content.
|
|
207
|
+
|
|
208
|
+
## Creating Checkout Sessions
|
|
209
|
+
|
|
210
|
+
`createCheckoutSession` accepts cart items keyed by product ID. The helper resolves Stripe price IDs by checking live/test fields on each product:
|
|
211
|
+
|
|
212
|
+
```typescript
|
|
213
|
+
const checkout = await createCheckoutSession({
|
|
214
|
+
client,
|
|
215
|
+
siteName: 'museum-indian-art',
|
|
216
|
+
items: [
|
|
217
|
+
{ productId: products[0].id, quantity: 1 },
|
|
218
|
+
{ productId: products[1].id, quantity: 2 }
|
|
219
|
+
],
|
|
220
|
+
successUrl: 'https://example.com/success',
|
|
221
|
+
cancelUrl: 'https://example.com/cancel',
|
|
222
|
+
customerEmail: 'customer@example.com',
|
|
223
|
+
currency: 'usd',
|
|
224
|
+
shippingAmount: 500,
|
|
225
|
+
shippingAddress: {
|
|
226
|
+
country: 'US',
|
|
227
|
+
state: 'CA',
|
|
228
|
+
postal_code: '94110'
|
|
229
|
+
},
|
|
230
|
+
billingAddress: {
|
|
231
|
+
country: 'US',
|
|
232
|
+
state: 'CA',
|
|
233
|
+
postal_code: '94110'
|
|
234
|
+
},
|
|
235
|
+
mode: 'test', // optional, defaults to 'live'
|
|
236
|
+
metadata: {
|
|
237
|
+
orderRef: 'web-2024-00042',
|
|
238
|
+
giftWrap: true,
|
|
239
|
+
shippingOverride: {
|
|
240
|
+
method: 'courier',
|
|
241
|
+
window: '5-7pm'
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
});
|
|
245
|
+
|
|
246
|
+
if ('error' in checkout) {
|
|
247
|
+
throw new Error(checkout.error);
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
console.log(checkout.data?.url); // Stripe-hosted checkout URL
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
Metadata values can be strings, numbers, booleans, arrays, or nested objects—the SDK forwards them as-is and PerspectAPI handles normalization before sending them to Stripe.
|
|
254
|
+
|
|
255
|
+
When `shippingAmount`, `shippingAddress`, or `billingAddress` are provided, the helper mirrors them into both camelCase and snake_case fields so the PerspectAPI checkout endpoint receives the correct payload. Optional tax configuration (`CheckoutTaxRequest`) is also forwarded, including customer exemption claims, so the new tax engine can pre-compute totals before redirecting to Stripe.
|
|
256
|
+
|
|
257
|
+
### Custom Price Resolution
|
|
258
|
+
|
|
259
|
+
If you store Stripe price IDs elsewhere, supply a resolver:
|
|
260
|
+
|
|
261
|
+
```typescript
|
|
262
|
+
await createCheckoutSession({
|
|
263
|
+
...options,
|
|
264
|
+
items,
|
|
265
|
+
priceIdResolver: (product, mode) => {
|
|
266
|
+
return mode === 'test'
|
|
267
|
+
? product.metadata?.test_price_id
|
|
268
|
+
: product.metadata?.live_price_id;
|
|
269
|
+
},
|
|
270
|
+
successUrl,
|
|
271
|
+
cancelUrl
|
|
272
|
+
});
|
|
273
|
+
```
|
|
274
|
+
|
|
275
|
+
## Type Reference
|
|
276
|
+
|
|
277
|
+
- `Product` now includes optional `product`, `name`, `slug`, `slug_prefix`, media arrays, and Stripe/Gateway identifiers.
|
|
278
|
+
- `MediaItem` describes the flattened media structure (ID, link, dimensions, etc.).
|
|
279
|
+
- `BlogPost` normalises PerspectAPI content payloads for blog/page usage.
|
|
280
|
+
- Loader option types:
|
|
281
|
+
- `LoaderOptions`
|
|
282
|
+
- `LoadProductsOptions`
|
|
283
|
+
- `LoadProductBySlugOptions`
|
|
284
|
+
- `LoadContentOptions`
|
|
285
|
+
- `LoadContentBySlugOptions`
|
|
286
|
+
- `CheckoutSessionOptions`
|
|
287
|
+
|
|
288
|
+
They are all exported from the SDK root for convenience:
|
|
289
|
+
|
|
290
|
+
```typescript
|
|
291
|
+
import type {
|
|
292
|
+
Product,
|
|
293
|
+
MediaItem,
|
|
294
|
+
BlogPost,
|
|
295
|
+
LoaderOptions,
|
|
296
|
+
LoadProductsOptions,
|
|
297
|
+
CheckoutAddress,
|
|
298
|
+
CheckoutSessionOptions
|
|
299
|
+
} from 'perspectapi-ts-sdk';
|
|
300
|
+
```
|
|
301
|
+
|
|
302
|
+
---
|
|
303
|
+
|
|
304
|
+
Need more examples? Open an issue or start a discussion on GitHub—we’d love to add additional recipes tailored to your stack.
|