@itm-studio/partner-sdk 0.1.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 +259 -0
- package/dist/index.d.mts +486 -0
- package/dist/index.d.ts +486 -0
- package/dist/index.js +1098 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +1094 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +53 -0
- package/schema/partner.graphql +439 -0
package/README.md
ADDED
|
@@ -0,0 +1,259 @@
|
|
|
1
|
+
# @itm-studio/partner-sdk
|
|
2
|
+
|
|
3
|
+
Type-safe TypeScript SDK for the ITM Partner API. Built with [genql](https://genql.dev/) — full autocomplete, zero introspection required.
|
|
4
|
+
|
|
5
|
+
[](https://github.com/itm-studio/itm-partner-sdk/actions/workflows/ci.yml)
|
|
6
|
+
[](https://app.graphql-hive.com/itm/itm-partner-api/production)
|
|
7
|
+
|
|
8
|
+
## Install
|
|
9
|
+
|
|
10
|
+
```bash
|
|
11
|
+
npm install @itm-studio/partner-sdk
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
## Quick Start
|
|
15
|
+
|
|
16
|
+
```typescript
|
|
17
|
+
import { createITMPartnerClient } from '@itm-studio/partner-sdk';
|
|
18
|
+
|
|
19
|
+
const itm = createITMPartnerClient({
|
|
20
|
+
token: process.env.ITM_PARTNER_TOKEN!,
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
// Get all upcoming moments for your brand
|
|
24
|
+
const { getPartnerMomentsForBrand } = await itm.query({
|
|
25
|
+
getPartnerMomentsForBrand: {
|
|
26
|
+
__args: { status: 'UPCOMING', take: 10 },
|
|
27
|
+
moments: {
|
|
28
|
+
uid: true,
|
|
29
|
+
name: true,
|
|
30
|
+
slug: true,
|
|
31
|
+
startDate: true,
|
|
32
|
+
endDate: true,
|
|
33
|
+
status: true,
|
|
34
|
+
venue: {
|
|
35
|
+
name: true,
|
|
36
|
+
city: true,
|
|
37
|
+
country: true,
|
|
38
|
+
},
|
|
39
|
+
ticketTiers: {
|
|
40
|
+
name: true,
|
|
41
|
+
price: true,
|
|
42
|
+
currency: { code: true, symbol: true },
|
|
43
|
+
remainingSupply: true,
|
|
44
|
+
},
|
|
45
|
+
},
|
|
46
|
+
totalCount: true,
|
|
47
|
+
hasNextPage: true,
|
|
48
|
+
nextCursor: true,
|
|
49
|
+
},
|
|
50
|
+
});
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
## Authentication
|
|
54
|
+
|
|
55
|
+
Get your partner API token from the ITM backstage dashboard: **Settings > Partner API**.
|
|
56
|
+
|
|
57
|
+
The token is sent as the `x-partner-api-key` header on every request. Your brand is determined automatically from the token — no need to pass brand IDs.
|
|
58
|
+
|
|
59
|
+
## Available Queries
|
|
60
|
+
|
|
61
|
+
### `getPartnerMomentsForBrand`
|
|
62
|
+
|
|
63
|
+
Get paginated moments (events) for your brand.
|
|
64
|
+
|
|
65
|
+
```typescript
|
|
66
|
+
const { getPartnerMomentsForBrand } = await itm.query({
|
|
67
|
+
getPartnerMomentsForBrand: {
|
|
68
|
+
__args: {
|
|
69
|
+
take: 20, // items per page (default: 50)
|
|
70
|
+
cursor: nextCursor, // from previous response
|
|
71
|
+
status: 'UPCOMING', // UPCOMING | LIVE | ENDED
|
|
72
|
+
sortOrder: 'ASC', // ASC | DESC (by start date)
|
|
73
|
+
},
|
|
74
|
+
moments: { uid: true, name: true, startDate: true },
|
|
75
|
+
totalCount: true,
|
|
76
|
+
hasNextPage: true,
|
|
77
|
+
nextCursor: true,
|
|
78
|
+
},
|
|
79
|
+
});
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
### `getPartnerMoment`
|
|
83
|
+
|
|
84
|
+
Get a single moment by slug.
|
|
85
|
+
|
|
86
|
+
```typescript
|
|
87
|
+
const { getPartnerMoment } = await itm.query({
|
|
88
|
+
getPartnerMoment: {
|
|
89
|
+
__args: { slug: 'my-event-slug' },
|
|
90
|
+
uid: true,
|
|
91
|
+
name: true,
|
|
92
|
+
startDate: true,
|
|
93
|
+
endDate: true,
|
|
94
|
+
ticketTiers: {
|
|
95
|
+
name: true,
|
|
96
|
+
price: true,
|
|
97
|
+
numSold: true,
|
|
98
|
+
remainingSupply: true,
|
|
99
|
+
},
|
|
100
|
+
},
|
|
101
|
+
});
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
### `getPartnerTicketsForMoment`
|
|
105
|
+
|
|
106
|
+
Get paginated tickets for a moment.
|
|
107
|
+
|
|
108
|
+
```typescript
|
|
109
|
+
const { getPartnerTicketsForMoment } = await itm.query({
|
|
110
|
+
getPartnerTicketsForMoment: {
|
|
111
|
+
__args: {
|
|
112
|
+
momentSlug: 'my-event-slug',
|
|
113
|
+
checkedIn: true, // optional: filter by check-in status
|
|
114
|
+
take: 50,
|
|
115
|
+
},
|
|
116
|
+
tickets: {
|
|
117
|
+
uid: true,
|
|
118
|
+
price: true,
|
|
119
|
+
redeemedAt: true,
|
|
120
|
+
hasExpired: true,
|
|
121
|
+
user: { uid: true, name: true, primaryEmail: true },
|
|
122
|
+
ticketTier: { name: true, price: true },
|
|
123
|
+
payment: { amount: true, status: true },
|
|
124
|
+
},
|
|
125
|
+
totalCount: true,
|
|
126
|
+
hasNextPage: true,
|
|
127
|
+
nextCursor: true,
|
|
128
|
+
},
|
|
129
|
+
});
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
### `getPartnerCustomersForMoment`
|
|
133
|
+
|
|
134
|
+
Get paginated customers (users with their tickets) for a moment.
|
|
135
|
+
|
|
136
|
+
```typescript
|
|
137
|
+
const { getPartnerCustomersForMoment } = await itm.query({
|
|
138
|
+
getPartnerCustomersForMoment: {
|
|
139
|
+
__args: { momentSlug: 'my-event-slug', take: 50 },
|
|
140
|
+
customers: {
|
|
141
|
+
user: { uid: true, name: true, primaryEmail: true, phoneNumber: true },
|
|
142
|
+
tickets: { uid: true, price: true, redeemedAt: true },
|
|
143
|
+
},
|
|
144
|
+
totalCount: true,
|
|
145
|
+
hasNextPage: true,
|
|
146
|
+
nextCursor: true,
|
|
147
|
+
},
|
|
148
|
+
});
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
## Pagination
|
|
152
|
+
|
|
153
|
+
All list endpoints use cursor-based pagination:
|
|
154
|
+
|
|
155
|
+
```typescript
|
|
156
|
+
let cursor: string | null = null;
|
|
157
|
+
|
|
158
|
+
do {
|
|
159
|
+
const { getPartnerTicketsForMoment } = await itm.query({
|
|
160
|
+
getPartnerTicketsForMoment: {
|
|
161
|
+
__args: { momentSlug: 'my-event', cursor, take: 50 },
|
|
162
|
+
tickets: { uid: true, user: { name: true } },
|
|
163
|
+
hasNextPage: true,
|
|
164
|
+
nextCursor: true,
|
|
165
|
+
totalCount: true,
|
|
166
|
+
},
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
for (const ticket of getPartnerTicketsForMoment.tickets) {
|
|
170
|
+
console.log(ticket.user.name);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
cursor = getPartnerTicketsForMoment.nextCursor ?? null;
|
|
174
|
+
} while (cursor);
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
## Custom Base URL
|
|
178
|
+
|
|
179
|
+
For staging or local development:
|
|
180
|
+
|
|
181
|
+
```typescript
|
|
182
|
+
const itm = createITMPartnerClient({
|
|
183
|
+
token: process.env.ITM_PARTNER_TOKEN!,
|
|
184
|
+
baseUrl: 'https://staging-api.itm.studio/graphql',
|
|
185
|
+
});
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
## Development
|
|
189
|
+
|
|
190
|
+
```bash
|
|
191
|
+
npm install
|
|
192
|
+
npm run generate # Generate typed client from schema
|
|
193
|
+
npm test # Run tests
|
|
194
|
+
npm run build # Build for publishing
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
## Architecture
|
|
198
|
+
|
|
199
|
+
```
|
|
200
|
+
itm-backend (NestJS) itm-partner-sdk
|
|
201
|
+
┌─────────────────────┐ ┌────────────────────────┐
|
|
202
|
+
│ Full GraphQL Schema │ │ schema/partner.graphql │
|
|
203
|
+
│ (8000+ lines) │ ── export ──▶ │ (partner subset only) │
|
|
204
|
+
│ │ │ │
|
|
205
|
+
│ BrandPartnerGuard │ │ genql │
|
|
206
|
+
│ x-partner-api-key │ │ │ │
|
|
207
|
+
│ Authorization Bearer │ │ ▼ │
|
|
208
|
+
└─────────────────────┘ │ src/generated/ (typed) │
|
|
209
|
+
▲ │ │ │
|
|
210
|
+
│ │ ▼ │
|
|
211
|
+
│ x-partner-api-key │ createITMPartnerClient │
|
|
212
|
+
└──────────────────────────────│ │
|
|
213
|
+
└────────────────────────┘
|
|
214
|
+
│
|
|
215
|
+
▼
|
|
216
|
+
┌────────────────────────┐
|
|
217
|
+
│ GraphQL Hive │
|
|
218
|
+
│ Schema Registry │
|
|
219
|
+
│ Breaking change guard │
|
|
220
|
+
└────────────────────────┘
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
- **Partner schema** is a curated subset of the full backend schema — only partner-safe endpoints
|
|
224
|
+
- **genql** generates a fully typed client from the schema at build time — no runtime introspection
|
|
225
|
+
- **GraphQL Hive** validates schema changes on every PR and blocks breaking changes
|
|
226
|
+
- **Authentication** uses the existing `BrandPartnerGuard` — token sent via `x-partner-api-key` header
|
|
227
|
+
|
|
228
|
+
## Schema Governance (Hive)
|
|
229
|
+
|
|
230
|
+
This SDK uses [GraphQL Hive](https://the-guild.dev/graphql/hive) for schema governance.
|
|
231
|
+
|
|
232
|
+
- **On PRs**: `schema:check` runs automatically to detect breaking changes before merge
|
|
233
|
+
- **On push to main**: `schema:publish` updates the registry so the schema history is tracked
|
|
234
|
+
- **Dashboard**: [app.graphql-hive.com/itm/itm-partner-api/production](https://app.graphql-hive.com/itm/itm-partner-api/production)
|
|
235
|
+
|
|
236
|
+
```bash
|
|
237
|
+
# Local usage (requires HIVE_TOKEN env var)
|
|
238
|
+
npm run schema:check # Check for breaking changes
|
|
239
|
+
npm run schema:publish # Publish schema to registry
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
## CI/CD
|
|
243
|
+
|
|
244
|
+
| Job | Trigger | What it does |
|
|
245
|
+
|---|---|---|
|
|
246
|
+
| `build-and-test` | Every push & PR | Install, generate, lint, test, build |
|
|
247
|
+
| `schema-check` | PRs only | Validates schema changes won't break partners |
|
|
248
|
+
| `schema-publish` | Push to main | Publishes schema to Hive registry |
|
|
249
|
+
| `publish` | Push to main | Auto-bumps patch version and publishes to NPM |
|
|
250
|
+
|
|
251
|
+
## Adding New Partner Endpoints
|
|
252
|
+
|
|
253
|
+
When exposing a new backend endpoint to partners:
|
|
254
|
+
|
|
255
|
+
1. Add the query/mutation to `schema/partner.graphql` with only the fields partners should access
|
|
256
|
+
2. Run `npm run generate` to regenerate the typed client
|
|
257
|
+
3. Add tests in `tests/queries.test.ts`
|
|
258
|
+
4. Open a PR — Hive will validate the schema change isn't breaking
|
|
259
|
+
5. After merge, the schema is auto-published to Hive and a new SDK version can be released
|