@plyaz/api 1.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 +693 -0
- package/dist/index.cjs +10 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.mjs +8 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +138 -0
package/README.md
ADDED
|
@@ -0,0 +1,693 @@
|
|
|
1
|
+
# @plyaz/api
|
|
2
|
+
|
|
3
|
+
Unified API client for seamless backend communication across the Plyaz platform. Provides type-safe endpoints, React Query integration, consistent error handling, and intelligent request management - supporting Web3, authentication, and tokenized experiences.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
The @plyaz/api package provides a unified API client for communicating with backend services across the Plyaz platform. It offers type-safe API endpoints, consistent error handling, and integration with React Query for efficient data fetching. This package serves as the core communication layer between frontend applications and backend services.
|
|
8
|
+
|
|
9
|
+
> **Note**: This package is designed as a shared package that works across frontend and backend contexts, with platform-specific implementations where necessary. It integrates seamlessly with the @plyaz/* ecosystem of shared packages.
|
|
10
|
+
|
|
11
|
+
## Features
|
|
12
|
+
|
|
13
|
+
- **Type-Safe API Client**: Built on `fetchff` with full TypeScript support
|
|
14
|
+
- **React Query Integration**: Ready-to-use hooks for data fetching and mutations
|
|
15
|
+
- **Consistent Error Handling**: Integrated with `@plyaz/errors` for standardized errors
|
|
16
|
+
- **Request Management**: Deduplication, cancellation, and retry mechanisms
|
|
17
|
+
- **Authentication Support**: Built-in auth token handling and interceptors
|
|
18
|
+
- **Pagination Support**: Cursor and offset-based pagination utilities
|
|
19
|
+
- **Performance Optimized**: Request batching, caching, and queue management
|
|
20
|
+
- **Event Integration**: Lifecycle events via `@plyaz/events`
|
|
21
|
+
- **Third-Party APIs**: Support for Alchemy, The Graph, and other integrations
|
|
22
|
+
|
|
23
|
+
## Installation
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
pnpm add @plyaz/api
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## Development
|
|
30
|
+
|
|
31
|
+
### Prerequisites
|
|
32
|
+
|
|
33
|
+
- Node.js >= 22.4.0
|
|
34
|
+
- pnpm >= 8.0.0
|
|
35
|
+
|
|
36
|
+
### Setup
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
# Clone the repository
|
|
40
|
+
git clone https://github.com/Plyaz-Official/api.git
|
|
41
|
+
cd api
|
|
42
|
+
|
|
43
|
+
# Install dependencies
|
|
44
|
+
pnpm install
|
|
45
|
+
|
|
46
|
+
# Start development (watch mode)
|
|
47
|
+
pnpm dev
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
### Available Scripts
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
# Development
|
|
54
|
+
pnpm dev # Start development with watch mode (alias: pnpm build:watch)
|
|
55
|
+
pnpm build:watch # Watch files and rebuild on changes
|
|
56
|
+
|
|
57
|
+
# Building
|
|
58
|
+
pnpm build # Full build (clean + JS + TypeScript declarations)
|
|
59
|
+
pnpm build:js # Build JavaScript bundles with tsup
|
|
60
|
+
pnpm build:types # Generate TypeScript declaration files
|
|
61
|
+
pnpm clean # Remove dist directory
|
|
62
|
+
|
|
63
|
+
# Testing
|
|
64
|
+
pnpm test # Run all tests once
|
|
65
|
+
pnpm test:watch # Run tests in watch mode
|
|
66
|
+
pnpm test:coverage # Run tests with coverage report
|
|
67
|
+
pnpm test:ui # Open Vitest UI for interactive testing
|
|
68
|
+
pnpm test:ci # Run tests in CI environment
|
|
69
|
+
|
|
70
|
+
# Code Quality
|
|
71
|
+
pnpm lint # Check code with ESLint
|
|
72
|
+
pnpm lint:fix # Auto-fix ESLint issues
|
|
73
|
+
pnpm format # Format code with Prettier
|
|
74
|
+
pnpm format:check # Check if code is formatted correctly
|
|
75
|
+
pnpm type:check # Type check with TypeScript (no emit)
|
|
76
|
+
|
|
77
|
+
# Security & Auditing
|
|
78
|
+
pnpm audit # Basic security audit
|
|
79
|
+
pnpm audit:moderate # Check for moderate+ vulnerabilities
|
|
80
|
+
pnpm audit:high # Check for high+ vulnerabilities
|
|
81
|
+
pnpm audit:critical # Check only critical vulnerabilities
|
|
82
|
+
pnpm audit:fix # Auto-fix vulnerabilities where possible
|
|
83
|
+
pnpm audit:enhanced # Enhanced audit with audit-ci
|
|
84
|
+
pnpm security:check # Full security check (moderate + audit-ci)
|
|
85
|
+
|
|
86
|
+
# Publishing
|
|
87
|
+
pnpm prepublishOnly # Runs build, test, and lint before publishing
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
## Package Structure
|
|
91
|
+
|
|
92
|
+
```
|
|
93
|
+
@plyaz/api/
|
|
94
|
+
├── src/
|
|
95
|
+
│ ├── client/ # API client implementation
|
|
96
|
+
│ │ ├── createApiClient.ts # Factory function for API client
|
|
97
|
+
│ │ ├── defaultConfig.ts # Default configuration
|
|
98
|
+
│ │ └── types.ts # Client-specific types
|
|
99
|
+
│ ├── endpoints/ # API endpoint definitions
|
|
100
|
+
│ │ ├── auth.ts # Authentication endpoints
|
|
101
|
+
│ │ ├── user.ts # User endpoints
|
|
102
|
+
│ │ ├── nft.ts # NFT endpoints
|
|
103
|
+
│ │ └── transaction.ts # Transaction endpoints
|
|
104
|
+
│ ├── services/ # API service functions
|
|
105
|
+
│ │ ├── auth.ts # Auth service functions
|
|
106
|
+
│ │ ├── user.ts # User service functions
|
|
107
|
+
│ │ └── nft.ts # NFT service functions
|
|
108
|
+
│ ├── integrations/ # Third-party API integrations
|
|
109
|
+
│ │ └── alchemy/ # Alchemy API integration
|
|
110
|
+
│ ├── hooks/ # React Query hooks
|
|
111
|
+
│ │ ├── auth/ # Auth-related hooks
|
|
112
|
+
│ │ └── user/ # User-related hooks
|
|
113
|
+
│ ├── utils/ # Utility functions
|
|
114
|
+
│ │ ├── error-handler.ts # Error handling utilities
|
|
115
|
+
│ │ └── pagination.ts # Pagination utilities
|
|
116
|
+
│ └── index.ts # Main exports
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
## Quick Start
|
|
120
|
+
|
|
121
|
+
### Basic Setup
|
|
122
|
+
|
|
123
|
+
#### Initialize API Client
|
|
124
|
+
|
|
125
|
+
```typescript
|
|
126
|
+
import { createApiClient } from '@plyaz/api';
|
|
127
|
+
|
|
128
|
+
// Create API client with custom configuration
|
|
129
|
+
const api = createApiClient({
|
|
130
|
+
baseURL: process.env.NEXT_PUBLIC_API_URL,
|
|
131
|
+
timeout: 30000,
|
|
132
|
+
headers: {
|
|
133
|
+
'Content-Type': 'application/json'
|
|
134
|
+
}
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
// Or use the default pre-configured client
|
|
138
|
+
import { api } from '@plyaz/api';
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
#### With Authentication
|
|
142
|
+
|
|
143
|
+
```typescript
|
|
144
|
+
import { createApiClient } from '@plyaz/api';
|
|
145
|
+
|
|
146
|
+
const api = createApiClient({
|
|
147
|
+
onRequest: (config) => {
|
|
148
|
+
const token = localStorage.getItem('auth_token');
|
|
149
|
+
if (token) {
|
|
150
|
+
config.headers = {
|
|
151
|
+
...config.headers,
|
|
152
|
+
Authorization: `Bearer ${token}`
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
return config;
|
|
156
|
+
}
|
|
157
|
+
});
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
### React/Next.js Integration
|
|
161
|
+
|
|
162
|
+
#### Query Hook Usage
|
|
163
|
+
|
|
164
|
+
```typescript
|
|
165
|
+
import { useGetUserProfile } from '@plyaz/api/hooks';
|
|
166
|
+
|
|
167
|
+
export function UserProfile({ walletAddress }: { walletAddress: string }) {
|
|
168
|
+
const { data, error, isLoading } = useGetUserProfile(
|
|
169
|
+
{ walletAddress },
|
|
170
|
+
{ includeDetails: true }
|
|
171
|
+
);
|
|
172
|
+
|
|
173
|
+
if (isLoading) return <div>Loading...</div>;
|
|
174
|
+
if (error) return <div>Error: {error.message}</div>;
|
|
175
|
+
|
|
176
|
+
return (
|
|
177
|
+
<div>
|
|
178
|
+
<h1>{data.username}</h1>
|
|
179
|
+
<p>{data.email}</p>
|
|
180
|
+
</div>
|
|
181
|
+
);
|
|
182
|
+
}
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
#### Mutation Hook Usage
|
|
186
|
+
|
|
187
|
+
```typescript
|
|
188
|
+
import { useUpdateUserProfile } from '@plyaz/api/hooks';
|
|
189
|
+
|
|
190
|
+
export function ProfileEditForm({ userId }: { userId: string }) {
|
|
191
|
+
const { mutate, isLoading } = useUpdateUserProfile();
|
|
192
|
+
|
|
193
|
+
const handleSubmit = (formData: UpdateUserRequest) => {
|
|
194
|
+
mutate(
|
|
195
|
+
{
|
|
196
|
+
pathParams: { userId },
|
|
197
|
+
body: formData
|
|
198
|
+
},
|
|
199
|
+
{
|
|
200
|
+
onSuccess: (data) => {
|
|
201
|
+
console.log('Profile updated:', data);
|
|
202
|
+
},
|
|
203
|
+
onError: (error) => {
|
|
204
|
+
console.error('Update failed:', error);
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
);
|
|
208
|
+
};
|
|
209
|
+
|
|
210
|
+
return (
|
|
211
|
+
<form onSubmit={handleSubmit}>
|
|
212
|
+
{/* Form fields */}
|
|
213
|
+
<button type="submit" disabled={isLoading}>
|
|
214
|
+
{isLoading ? 'Updating...' : 'Update Profile'}
|
|
215
|
+
</button>
|
|
216
|
+
</form>
|
|
217
|
+
);
|
|
218
|
+
}
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
#### Infinite Query for Pagination
|
|
222
|
+
|
|
223
|
+
```typescript
|
|
224
|
+
import { useInfiniteTransactions } from '@plyaz/api/hooks';
|
|
225
|
+
|
|
226
|
+
export function TransactionList({ walletAddress, chainId }: Props) {
|
|
227
|
+
const {
|
|
228
|
+
data,
|
|
229
|
+
fetchNextPage,
|
|
230
|
+
hasNextPage,
|
|
231
|
+
isFetchingNextPage
|
|
232
|
+
} = useInfiniteTransactions(
|
|
233
|
+
{ walletAddress, chainId },
|
|
234
|
+
{ limit: 10 }
|
|
235
|
+
);
|
|
236
|
+
|
|
237
|
+
return (
|
|
238
|
+
<div>
|
|
239
|
+
{data?.pages.flatMap(page =>
|
|
240
|
+
page.transactions.map(tx => (
|
|
241
|
+
<div key={tx.id}>{tx.hash}</div>
|
|
242
|
+
))
|
|
243
|
+
)}
|
|
244
|
+
|
|
245
|
+
{hasNextPage && (
|
|
246
|
+
<button onClick={() => fetchNextPage()}>
|
|
247
|
+
{isFetchingNextPage ? 'Loading...' : 'Load More'}
|
|
248
|
+
</button>
|
|
249
|
+
)}
|
|
250
|
+
</div>
|
|
251
|
+
);
|
|
252
|
+
}
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
### Service Functions
|
|
256
|
+
|
|
257
|
+
```typescript
|
|
258
|
+
import { getUserProfile, updateUserProfile } from '@plyaz/api/services';
|
|
259
|
+
|
|
260
|
+
// Fetch user profile
|
|
261
|
+
const profile = await getUserProfile(
|
|
262
|
+
{ walletAddress: '0x123...' },
|
|
263
|
+
{ includeDetails: true }
|
|
264
|
+
);
|
|
265
|
+
|
|
266
|
+
// Update user profile
|
|
267
|
+
const updated = await updateUserProfile(
|
|
268
|
+
{ userId: 'usr-123' },
|
|
269
|
+
{ username: 'newusername', email: 'new@email.com' }
|
|
270
|
+
);
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
## API Methods
|
|
274
|
+
|
|
275
|
+
### GET Requests
|
|
276
|
+
|
|
277
|
+
```typescript
|
|
278
|
+
// Path parameters
|
|
279
|
+
const { data } = await api.getUserProfile({
|
|
280
|
+
urlPathParams: { walletAddress: '0x123...' }
|
|
281
|
+
});
|
|
282
|
+
|
|
283
|
+
// Query parameters
|
|
284
|
+
const { data } = await api.getUsers({
|
|
285
|
+
params: { page: 1, limit: 10 }
|
|
286
|
+
});
|
|
287
|
+
|
|
288
|
+
// Both path and query parameters
|
|
289
|
+
const { data } = await api.getNFTs({
|
|
290
|
+
urlPathParams: { walletAddress: '0x123...', chainId: 'ethereum' },
|
|
291
|
+
params: { tokenType: 'ERC721' }
|
|
292
|
+
});
|
|
293
|
+
```
|
|
294
|
+
|
|
295
|
+
### POST Requests
|
|
296
|
+
|
|
297
|
+
```typescript
|
|
298
|
+
// Simple POST
|
|
299
|
+
const { data } = await api.createUser({
|
|
300
|
+
body: { username: 'johndoe', email: 'john@example.com' }
|
|
301
|
+
});
|
|
302
|
+
|
|
303
|
+
// POST with path parameters
|
|
304
|
+
const { data } = await api.transferNFT({
|
|
305
|
+
urlPathParams: { walletAddress: '0x456...' },
|
|
306
|
+
body: { nftId: '123', toAddress: '0x789...' }
|
|
307
|
+
});
|
|
308
|
+
```
|
|
309
|
+
|
|
310
|
+
### PUT/PATCH Requests
|
|
311
|
+
|
|
312
|
+
```typescript
|
|
313
|
+
// PUT request
|
|
314
|
+
const { data } = await api.updateUserProfile({
|
|
315
|
+
urlPathParams: { userId: '123' },
|
|
316
|
+
body: { username: 'newusername' }
|
|
317
|
+
});
|
|
318
|
+
|
|
319
|
+
// PATCH request
|
|
320
|
+
const { data } = await api.patchUserSettings({
|
|
321
|
+
urlPathParams: { userId: '123' },
|
|
322
|
+
body: { theme: 'dark' }
|
|
323
|
+
});
|
|
324
|
+
```
|
|
325
|
+
|
|
326
|
+
### DELETE Requests
|
|
327
|
+
|
|
328
|
+
```typescript
|
|
329
|
+
// DELETE with path parameters
|
|
330
|
+
const { data } = await api.deleteUser({
|
|
331
|
+
urlPathParams: { userId: '123' }
|
|
332
|
+
});
|
|
333
|
+
```
|
|
334
|
+
|
|
335
|
+
## Configuration
|
|
336
|
+
|
|
337
|
+
### Full Configuration Options
|
|
338
|
+
|
|
339
|
+
```typescript
|
|
340
|
+
{
|
|
341
|
+
// Base configuration
|
|
342
|
+
baseURL: process.env.NEXT_PUBLIC_API_URL,
|
|
343
|
+
timeout: 30000,
|
|
344
|
+
headers: {
|
|
345
|
+
'Content-Type': 'application/json'
|
|
346
|
+
},
|
|
347
|
+
|
|
348
|
+
// Request management
|
|
349
|
+
withCredentials: true,
|
|
350
|
+
cancellable: true,
|
|
351
|
+
dedupeTime: 1000,
|
|
352
|
+
|
|
353
|
+
// Retry configuration
|
|
354
|
+
retry: {
|
|
355
|
+
retries: 3,
|
|
356
|
+
delay: 1000,
|
|
357
|
+
backoff: 1.5,
|
|
358
|
+
maxDelay: 10000,
|
|
359
|
+
retryOn: [408, 429, 500, 502, 503, 504]
|
|
360
|
+
},
|
|
361
|
+
|
|
362
|
+
// Error handling strategy
|
|
363
|
+
strategy: 'softFail', // 'reject' | 'softFail' | 'defaultResponse' | 'silent'
|
|
364
|
+
|
|
365
|
+
// Caching
|
|
366
|
+
cache: {
|
|
367
|
+
cacheTime: 60,
|
|
368
|
+
skipCache: (response, config) => config.method !== 'GET'
|
|
369
|
+
},
|
|
370
|
+
|
|
371
|
+
// Interceptors
|
|
372
|
+
onRequest: (config) => config,
|
|
373
|
+
onResponse: (response) => response,
|
|
374
|
+
onError: (error) => error
|
|
375
|
+
}
|
|
376
|
+
```
|
|
377
|
+
|
|
378
|
+
## Advanced Features
|
|
379
|
+
|
|
380
|
+
### Request Cancellation
|
|
381
|
+
|
|
382
|
+
```typescript
|
|
383
|
+
// Enable automatic cancellation
|
|
384
|
+
const { data } = await api.searchUsers({
|
|
385
|
+
params: { query: searchTerm },
|
|
386
|
+
cancellable: true,
|
|
387
|
+
rejectCancelled: true
|
|
388
|
+
});
|
|
389
|
+
```
|
|
390
|
+
|
|
391
|
+
### Request Deduplication
|
|
392
|
+
|
|
393
|
+
```typescript
|
|
394
|
+
// Deduplicate identical requests within time window
|
|
395
|
+
const { data } = await api.getUserProfile({
|
|
396
|
+
urlPathParams: { walletAddress: '0x123...' },
|
|
397
|
+
dedupeTime: 5000 // 5 seconds
|
|
398
|
+
});
|
|
399
|
+
```
|
|
400
|
+
|
|
401
|
+
### Error Handling Strategies
|
|
402
|
+
|
|
403
|
+
```typescript
|
|
404
|
+
// Reject strategy (throws errors)
|
|
405
|
+
try {
|
|
406
|
+
const { data } = await api.getUserProfile({
|
|
407
|
+
urlPathParams: { walletAddress: '0x123...' },
|
|
408
|
+
strategy: 'reject'
|
|
409
|
+
});
|
|
410
|
+
} catch (error) {
|
|
411
|
+
console.error(error);
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
// SoftFail strategy (returns error property)
|
|
415
|
+
const { data, error } = await api.getUserProfile({
|
|
416
|
+
urlPathParams: { walletAddress: '0x123...' },
|
|
417
|
+
strategy: 'softFail'
|
|
418
|
+
});
|
|
419
|
+
|
|
420
|
+
if (error) {
|
|
421
|
+
console.error(error);
|
|
422
|
+
} else {
|
|
423
|
+
console.log(data);
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
// Silent strategy (fire-and-forget)
|
|
427
|
+
api.trackUserActivity({
|
|
428
|
+
body: { action: 'page_view' },
|
|
429
|
+
strategy: 'silent'
|
|
430
|
+
});
|
|
431
|
+
```
|
|
432
|
+
|
|
433
|
+
### Custom Interceptors
|
|
434
|
+
|
|
435
|
+
```typescript
|
|
436
|
+
const apiWithAuth = createApiClient({
|
|
437
|
+
onRequest: [
|
|
438
|
+
// Add authentication
|
|
439
|
+
(config) => {
|
|
440
|
+
const token = getAuthToken();
|
|
441
|
+
if (token) {
|
|
442
|
+
config.headers.Authorization = `Bearer ${token}`;
|
|
443
|
+
}
|
|
444
|
+
return config;
|
|
445
|
+
},
|
|
446
|
+
// Add tenant ID
|
|
447
|
+
(config) => {
|
|
448
|
+
config.headers['X-Tenant-ID'] = getTenantId();
|
|
449
|
+
return config;
|
|
450
|
+
}
|
|
451
|
+
],
|
|
452
|
+
|
|
453
|
+
onError: (error) => {
|
|
454
|
+
if (error.status === 401) {
|
|
455
|
+
router.push('/login');
|
|
456
|
+
}
|
|
457
|
+
return error;
|
|
458
|
+
}
|
|
459
|
+
});
|
|
460
|
+
```
|
|
461
|
+
|
|
462
|
+
## Error Handling
|
|
463
|
+
|
|
464
|
+
### With @plyaz/errors Integration
|
|
465
|
+
|
|
466
|
+
```typescript
|
|
467
|
+
import { isNotFoundError, isValidationError } from '@plyaz/errors';
|
|
468
|
+
import { handleApiError } from '@plyaz/api/utils';
|
|
469
|
+
|
|
470
|
+
const fetchUser = async (walletAddress: string) => {
|
|
471
|
+
const { data, error } = await api.getUserProfile({
|
|
472
|
+
urlPathParams: { walletAddress },
|
|
473
|
+
strategy: 'softFail'
|
|
474
|
+
});
|
|
475
|
+
|
|
476
|
+
if (error) {
|
|
477
|
+
if (isNotFoundError(error)) {
|
|
478
|
+
console.log('User not found');
|
|
479
|
+
} else if (isValidationError(error)) {
|
|
480
|
+
console.log('Invalid parameters');
|
|
481
|
+
} else {
|
|
482
|
+
const handled = handleApiError(error);
|
|
483
|
+
console.error(handled.message);
|
|
484
|
+
}
|
|
485
|
+
return null;
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
return data;
|
|
489
|
+
};
|
|
490
|
+
```
|
|
491
|
+
|
|
492
|
+
## Pagination Utilities
|
|
493
|
+
|
|
494
|
+
```typescript
|
|
495
|
+
import {
|
|
496
|
+
getCursorPaginationParams,
|
|
497
|
+
getOffsetPaginationParams,
|
|
498
|
+
getPaginationInfo
|
|
499
|
+
} from '@plyaz/api/utils';
|
|
500
|
+
|
|
501
|
+
// Cursor-based pagination
|
|
502
|
+
const cursorParams = getCursorPaginationParams('next_cursor_123', 10);
|
|
503
|
+
|
|
504
|
+
// Offset-based pagination
|
|
505
|
+
const offsetParams = getOffsetPaginationParams(2, 10); // Page 2, 10 items
|
|
506
|
+
|
|
507
|
+
// Extract pagination info
|
|
508
|
+
const { data } = await api.getUsers({ params: offsetParams });
|
|
509
|
+
const info = getPaginationInfo(data);
|
|
510
|
+
console.log(`Page ${info.currentPage} of ${info.totalPages}`);
|
|
511
|
+
```
|
|
512
|
+
|
|
513
|
+
## Third-Party Integrations
|
|
514
|
+
|
|
515
|
+
### Alchemy API Integration
|
|
516
|
+
|
|
517
|
+
```typescript
|
|
518
|
+
import { AlchemyService } from '@plyaz/api/integrations/alchemy';
|
|
519
|
+
|
|
520
|
+
const alchemy = new AlchemyService({
|
|
521
|
+
apiKey: process.env.ALCHEMY_API_KEY,
|
|
522
|
+
network: 'eth-mainnet'
|
|
523
|
+
});
|
|
524
|
+
|
|
525
|
+
// Get NFTs for wallet
|
|
526
|
+
const nfts = await alchemy.getNFTs({
|
|
527
|
+
owner: '0x123...',
|
|
528
|
+
withMetadata: true
|
|
529
|
+
});
|
|
530
|
+
|
|
531
|
+
// Get token balances
|
|
532
|
+
const balances = await alchemy.getTokenBalances({
|
|
533
|
+
address: '0x123...'
|
|
534
|
+
});
|
|
535
|
+
```
|
|
536
|
+
|
|
537
|
+
## Platform Architecture Context
|
|
538
|
+
|
|
539
|
+
The API client is a critical component of the Plyaz platform's shared package ecosystem:
|
|
540
|
+
|
|
541
|
+
### Frontend Architecture
|
|
542
|
+
- **Multiple Apps**: Supports web app, admin panel, and micro apps
|
|
543
|
+
- **React Query**: Deep integration for efficient data fetching
|
|
544
|
+
- **State Management**: Works with Zustand stores for state synchronization
|
|
545
|
+
- **Next.js**: Optimized for server-side rendering and API routes
|
|
546
|
+
|
|
547
|
+
### Backend Architecture
|
|
548
|
+
- **Microservices**: Communicates with distributed NestJS services
|
|
549
|
+
- **API Gateway**: Routes through centralized gateway with auth
|
|
550
|
+
- **Event-Driven**: Supports async communication patterns
|
|
551
|
+
- **GraphQL/REST**: Flexible endpoint configuration
|
|
552
|
+
|
|
553
|
+
## Integration with Other Plyaz Packages
|
|
554
|
+
|
|
555
|
+
| Package | Integration | Purpose |
|
|
556
|
+
|---------|------------|---------|
|
|
557
|
+
| **@plyaz/types** | Type definitions | Provides API interfaces and types |
|
|
558
|
+
| **@plyaz/errors** | Error handling | Standardized error types and handlers |
|
|
559
|
+
| **@plyaz/config** | Configuration | API URLs and environment settings |
|
|
560
|
+
| **@plyaz/auth** | Authentication | Token management and auth headers |
|
|
561
|
+
| **@plyaz/events** | Event bus | API lifecycle events |
|
|
562
|
+
| **@plyaz/logger** | Logging | Request/response logging |
|
|
563
|
+
| **@plyaz/web3** | Blockchain | Web3 transaction APIs |
|
|
564
|
+
| **@plyaz/monitoring** | Metrics | API performance tracking |
|
|
565
|
+
|
|
566
|
+
## Type Safety
|
|
567
|
+
|
|
568
|
+
### Endpoint Type Definitions
|
|
569
|
+
|
|
570
|
+
```typescript
|
|
571
|
+
// Types are imported from @plyaz/types
|
|
572
|
+
import type {
|
|
573
|
+
UserMethods,
|
|
574
|
+
TransactionMethods,
|
|
575
|
+
NFTMethods
|
|
576
|
+
} from '@plyaz/types/api';
|
|
577
|
+
|
|
578
|
+
// Combined interface for all endpoints
|
|
579
|
+
export interface EndpointsMethods extends
|
|
580
|
+
UserMethods,
|
|
581
|
+
TransactionMethods,
|
|
582
|
+
NFTMethods {}
|
|
583
|
+
```
|
|
584
|
+
|
|
585
|
+
### Request/Response Types
|
|
586
|
+
|
|
587
|
+
```typescript
|
|
588
|
+
import type {
|
|
589
|
+
ApiResponse,
|
|
590
|
+
PaginatedResponse,
|
|
591
|
+
ApiError
|
|
592
|
+
} from '@plyaz/types/api';
|
|
593
|
+
|
|
594
|
+
// Type-safe responses
|
|
595
|
+
type UserResponse = ApiResponse<User>;
|
|
596
|
+
type UsersListResponse = PaginatedResponse<User>;
|
|
597
|
+
```
|
|
598
|
+
|
|
599
|
+
## Best Practices
|
|
600
|
+
|
|
601
|
+
### Service Layer Pattern
|
|
602
|
+
|
|
603
|
+
```typescript
|
|
604
|
+
// services/user.ts
|
|
605
|
+
export async function getUserProfile(
|
|
606
|
+
urlPathParams: GetUserProfilePathParams,
|
|
607
|
+
queryParams?: GetUserProfileQueryParams
|
|
608
|
+
): Promise<UserProfile> {
|
|
609
|
+
return api.getUserProfile({
|
|
610
|
+
urlPathParams,
|
|
611
|
+
params: queryParams
|
|
612
|
+
});
|
|
613
|
+
}
|
|
614
|
+
```
|
|
615
|
+
|
|
616
|
+
### Query Key Management
|
|
617
|
+
|
|
618
|
+
```typescript
|
|
619
|
+
// hooks/user/keys.ts
|
|
620
|
+
export const userKeys = {
|
|
621
|
+
all: ['user'] as const,
|
|
622
|
+
lists: () => [...userKeys.all, 'list'] as const,
|
|
623
|
+
list: (params: any) => [...userKeys.lists(), params] as const,
|
|
624
|
+
profile: (id: string) => [...userKeys.all, 'profile', id] as const
|
|
625
|
+
};
|
|
626
|
+
```
|
|
627
|
+
|
|
628
|
+
### Error Boundary Integration
|
|
629
|
+
|
|
630
|
+
```typescript
|
|
631
|
+
import { ErrorBoundary } from '@plyaz/ui';
|
|
632
|
+
import { handleApiError } from '@plyaz/api/utils';
|
|
633
|
+
|
|
634
|
+
function MyApp() {
|
|
635
|
+
return (
|
|
636
|
+
<ErrorBoundary
|
|
637
|
+
fallback={<ErrorFallback />}
|
|
638
|
+
onError={(error) => {
|
|
639
|
+
const handled = handleApiError(error);
|
|
640
|
+
logError(handled);
|
|
641
|
+
}}
|
|
642
|
+
>
|
|
643
|
+
<App />
|
|
644
|
+
</ErrorBoundary>
|
|
645
|
+
);
|
|
646
|
+
}
|
|
647
|
+
```
|
|
648
|
+
|
|
649
|
+
## Migration Guide
|
|
650
|
+
|
|
651
|
+
### From Axios
|
|
652
|
+
|
|
653
|
+
```typescript
|
|
654
|
+
// Before (Axios)
|
|
655
|
+
const response = await axios.get('/api/users/123');
|
|
656
|
+
const user = response.data;
|
|
657
|
+
|
|
658
|
+
// After (@plyaz/api)
|
|
659
|
+
const { data: user } = await api.getUserProfile({
|
|
660
|
+
urlPathParams: { userId: '123' }
|
|
661
|
+
});
|
|
662
|
+
```
|
|
663
|
+
|
|
664
|
+
### From Fetch
|
|
665
|
+
|
|
666
|
+
```typescript
|
|
667
|
+
// Before (Fetch)
|
|
668
|
+
const response = await fetch('/api/users', {
|
|
669
|
+
method: 'POST',
|
|
670
|
+
headers: { 'Content-Type': 'application/json' },
|
|
671
|
+
body: JSON.stringify(userData)
|
|
672
|
+
});
|
|
673
|
+
const user = await response.json();
|
|
674
|
+
|
|
675
|
+
// After (@plyaz/api)
|
|
676
|
+
const { data: user } = await api.createUser({
|
|
677
|
+
body: userData
|
|
678
|
+
});
|
|
679
|
+
```
|
|
680
|
+
|
|
681
|
+
## Contributing
|
|
682
|
+
|
|
683
|
+
This package is part of the Plyaz platform. Please follow the contribution guidelines in the main repository.
|
|
684
|
+
|
|
685
|
+
## Resources
|
|
686
|
+
|
|
687
|
+
- [Plyaz Confluence Documentation](https://plyaz.atlassian.net/wiki/spaces/SD/pages/950316/API+Package)
|
|
688
|
+
- [fetchff Documentation](https://www.npmjs.com/package/fetchff)
|
|
689
|
+
- [React Query Documentation](https://tanstack.com/query)
|
|
690
|
+
|
|
691
|
+
## License
|
|
692
|
+
|
|
693
|
+
ISC © Plyaz
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"names":[],"mappings":";;;;;AAAO,IAAM,YAAA,GAAe","file":"index.cjs","sourcesContent":["export const PACKAGE_TEST = 1;\n"]}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,YAAY,IAAI,CAAC"}
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"names":[],"mappings":";;;AAAO,IAAM,YAAA,GAAe","file":"index.mjs","sourcesContent":["export const PACKAGE_TEST = 1;\n"]}
|
package/package.json
ADDED
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@plyaz/api",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "@plyaz/api ⚡ Core Plyaz API client enabling seamless Web3 interactions, authentication, and data access across apps and services. Built for scalability, reliability, and tokenized fan engagement experiences.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"keywords": [],
|
|
7
|
+
"packageManager": "pnpm@10.11.0",
|
|
8
|
+
"main": "./dist/index.cjs",
|
|
9
|
+
"module": "./dist/index.mjs",
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"exports": {
|
|
12
|
+
".": {
|
|
13
|
+
"types": "./dist/index.d.ts",
|
|
14
|
+
"import": "./dist/index.mjs",
|
|
15
|
+
"require": "./dist/index.cjs",
|
|
16
|
+
"default": "./dist/index.mjs"
|
|
17
|
+
},
|
|
18
|
+
"./package.json": "./package.json"
|
|
19
|
+
},
|
|
20
|
+
"engines": {
|
|
21
|
+
"node": ">=22.4.0",
|
|
22
|
+
"pnpm": ">=8.0.0"
|
|
23
|
+
},
|
|
24
|
+
"author": "Redeemer Pace",
|
|
25
|
+
"license": "ISC",
|
|
26
|
+
"files": [
|
|
27
|
+
"dist"
|
|
28
|
+
],
|
|
29
|
+
"dependencies": {
|
|
30
|
+
"@nestjs/common": "^11.1.3",
|
|
31
|
+
"@plyaz/config": "1.3.3",
|
|
32
|
+
"next": "15.4.7",
|
|
33
|
+
"react": "^19.1.0"
|
|
34
|
+
},
|
|
35
|
+
"devDependencies": {
|
|
36
|
+
"@changesets/cli": "^2.29.5",
|
|
37
|
+
"@darraghor/eslint-plugin-nestjs-typed": "^6.6.2",
|
|
38
|
+
"@eslint/eslintrc": "^3.2.0",
|
|
39
|
+
"@eslint/js": "^9.15.0",
|
|
40
|
+
"@eslint/markdown": "^6.5.0",
|
|
41
|
+
"@testing-library/jest-dom": "^6.6.3",
|
|
42
|
+
"@testing-library/react": "^16.3.0",
|
|
43
|
+
"@testing-library/user-event": "^14.6.1",
|
|
44
|
+
"@next/eslint-plugin-next": "^15.0.3",
|
|
45
|
+
"@plyaz/devtools": "^1.8.5",
|
|
46
|
+
"@plyaz/types": "^1.7.7",
|
|
47
|
+
"@types/node": "^22.14.0",
|
|
48
|
+
"@types/react": "^19.1.8",
|
|
49
|
+
"@types/react-dom": "^19.1.6",
|
|
50
|
+
"@typescript-eslint/eslint-plugin": "^8.15.0",
|
|
51
|
+
"@typescript-eslint/parser": "^8.15.0",
|
|
52
|
+
"@vitest/coverage-istanbul": "3.2.4",
|
|
53
|
+
"@vitest/coverage-v8": "^3.1.3",
|
|
54
|
+
"eslint": "^9.29.0",
|
|
55
|
+
"eslint-config-next": "^15.3.3",
|
|
56
|
+
"eslint-config-prettier": "^9.1.0",
|
|
57
|
+
"eslint-import-resolver-typescript": "^3.10.1",
|
|
58
|
+
"eslint-mdx": "^3.5.0",
|
|
59
|
+
"eslint-plugin-better-tailwindcss": "3.2.1",
|
|
60
|
+
"eslint-plugin-functional": "^4.4.1",
|
|
61
|
+
"eslint-plugin-import": "^2.31.0",
|
|
62
|
+
"eslint-plugin-jest": "^28.13.5",
|
|
63
|
+
"eslint-plugin-jsdoc": "^50.8.0",
|
|
64
|
+
"eslint-plugin-jsonc": "^2.20.1",
|
|
65
|
+
"eslint-plugin-jsx-a11y": "^6.10.2",
|
|
66
|
+
"eslint-plugin-markdownlint": "^0.9.0",
|
|
67
|
+
"eslint-plugin-mdx": "^3.5.0",
|
|
68
|
+
"eslint-plugin-n": "^17.20.0",
|
|
69
|
+
"eslint-plugin-prettier": "^5.5.0",
|
|
70
|
+
"eslint-plugin-promise": "^7.2.1",
|
|
71
|
+
"eslint-plugin-react": "^7.37.2",
|
|
72
|
+
"eslint-plugin-react-hooks": "^5.0.0",
|
|
73
|
+
"eslint-plugin-react-refresh": "^0.4.19",
|
|
74
|
+
"eslint-plugin-regexp": "^2.9.0",
|
|
75
|
+
"eslint-plugin-security": "^3.0.1",
|
|
76
|
+
"eslint-plugin-simple-import-sort": "^12.1.1",
|
|
77
|
+
"eslint-plugin-sonarjs": "^0.23.0",
|
|
78
|
+
"eslint-plugin-storybook": "^0.12.0",
|
|
79
|
+
"eslint-plugin-testing-library": "^6.5.0",
|
|
80
|
+
"eslint-plugin-unicorn": "^56.0.1",
|
|
81
|
+
"eslint-plugin-unused-imports": "^4.1.4",
|
|
82
|
+
"jiti": "^2.4.2",
|
|
83
|
+
"jsdom": "^26.1.0",
|
|
84
|
+
"markdownlint": "^0.38.0",
|
|
85
|
+
"next": "15.3.3",
|
|
86
|
+
"prettier": "^3.5.3",
|
|
87
|
+
"standard-version": "^9.5.0",
|
|
88
|
+
"tailwindcss": "^4.1.8",
|
|
89
|
+
"tsup": "8.5.0",
|
|
90
|
+
"typescript": "^5",
|
|
91
|
+
"typescript-eslint": "^8.34.0",
|
|
92
|
+
"vite": "^6.3.5",
|
|
93
|
+
"vite-plugin-dts": "^4.5.3",
|
|
94
|
+
"vitest": "3.2.4"
|
|
95
|
+
},
|
|
96
|
+
"peerDependenciesMeta": {
|
|
97
|
+
"react": {
|
|
98
|
+
"optional": true
|
|
99
|
+
},
|
|
100
|
+
"react-dom": {
|
|
101
|
+
"optional": true
|
|
102
|
+
},
|
|
103
|
+
"next": {
|
|
104
|
+
"optional": true
|
|
105
|
+
}
|
|
106
|
+
},
|
|
107
|
+
"scripts": {
|
|
108
|
+
"build": "pnpm clean && pnpm build:js && pnpm build:types",
|
|
109
|
+
"build:js": "tsup",
|
|
110
|
+
"build:types": "tsc -p tsconfig.build.json",
|
|
111
|
+
"build:watch": "tsup --watch",
|
|
112
|
+
"dev": "tsup --watch",
|
|
113
|
+
"lint": "eslint ./src ./tests",
|
|
114
|
+
"lint:fix": "eslint ./src ./tests --fix",
|
|
115
|
+
"format": "prettier --write './**/*.{ts,tsx,js,jsx}'",
|
|
116
|
+
"format:check": "prettier --check './**/*.{ts,tsx,js,jsx}'",
|
|
117
|
+
"type:check": "tsc --noEmit",
|
|
118
|
+
"test": "vitest run --no-watch",
|
|
119
|
+
"test:ci": "vitest run --no-watch",
|
|
120
|
+
"test:watch": "vitest --watch",
|
|
121
|
+
"test:coverage": "vitest run --coverage",
|
|
122
|
+
"test:ui": "vitest --ui",
|
|
123
|
+
"clean": "rm -rf dist",
|
|
124
|
+
"audit": "pnpm audit",
|
|
125
|
+
"audit:moderate": "pnpm audit --audit-level moderate",
|
|
126
|
+
"audit:high": "pnpm audit --audit-level high",
|
|
127
|
+
"audit:critical": "pnpm audit --audit-level critical",
|
|
128
|
+
"audit:fix": "pnpm audit --fix",
|
|
129
|
+
"audit:enhanced": "npx audit-ci --moderate",
|
|
130
|
+
"security:check": "pnpm audit:moderate && npx audit-ci --moderate",
|
|
131
|
+
"remove:unused": "pnpm remove:unused:fnc && pnpm remove:unused:interfaces",
|
|
132
|
+
"remove:unused:interfaces": "pnpm exec ./node_modules/@plyaz/devtools/scripts/check_types.ts --remove --report=./internal-reports/unused-types-report.md",
|
|
133
|
+
"remove:unused:fnc": "pnpm exec ./node_modules/@plyaz/devtools/scripts/check_fnc.ts --report=./internal-reports/unused-declarations.md",
|
|
134
|
+
"check:dependencies": "npx tsx ./node_modules/@plyaz/devtools/scripts/check_dependencies_versions.ts --report=./internal-reports/dependency-report.md",
|
|
135
|
+
"move:types": "npx tsx ./scripts/move_types.ts src/ --report=internal-reports/moved_types.md",
|
|
136
|
+
"move:constants": "npx tsx ./node_modules/@plyaz/devtools/scripts/move_constants.ts src/ --report=internal-reports/moved_consts.md"
|
|
137
|
+
}
|
|
138
|
+
}
|