@cushin/api-codegen 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/LICENSE ADDED
File without changes
package/README.md ADDED
@@ -0,0 +1,435 @@
1
+ # @cushin/api-codegen
2
+
3
+ Type-safe API client generator for React/Next.js applications with automatic hooks and server actions generation.
4
+
5
+ ## Features
6
+
7
+ - 🎯 **Type-Safe**: Full TypeScript support with Zod schema validation
8
+ - 🔄 **Auto-Generated**: Generate React Query hooks, Server Actions, and Server Queries
9
+ - 🚀 **Framework Agnostic**: Works with Vite, Next.js, and more
10
+ - 🔐 **Auth Built-in**: Token refresh, automatic retry with customizable callbacks
11
+ - 📦 **Zero Config**: Simple configuration with sensible defaults
12
+ - 🎨 **Customizable**: Custom templates and generation options
13
+
14
+ ## Installation
15
+
16
+ ```bash
17
+ npm install @cushin/api-codegen ky zod
18
+ # or
19
+ pnpm add @cushin/api-codegen ky zod
20
+ # or
21
+ yarn add @cushin/api-codegen ky zod
22
+ ```
23
+
24
+ For React Query support (client-side):
25
+ ```bash
26
+ npm install @tanstack/react-query
27
+ ```
28
+
29
+ ## Quick Start
30
+
31
+ ### 1. Initialize Configuration
32
+
33
+ ```bash
34
+ npx api-codegen init --provider vite
35
+ # or for Next.js
36
+ npx api-codegen init --provider nextjs
37
+ ```
38
+
39
+ This creates `api-codegen.config.js`:
40
+
41
+ ```js
42
+ /** @type {import('@cushin/api-codegen').UserConfig} */
43
+ export default {
44
+ provider: 'vite',
45
+ endpoints: './lib/api/config/endpoints.ts',
46
+ output: './lib/api/generated',
47
+ baseUrl: process.env.VITE_API_URL,
48
+ generateHooks: true,
49
+ generateClient: true,
50
+ };
51
+ ```
52
+
53
+ ### 2. Define Your API Endpoints
54
+
55
+ Create your endpoints configuration file:
56
+
57
+ ```typescript
58
+ // lib/api/config/endpoints.ts
59
+ import { z } from 'zod';
60
+ import { defineConfig, defineEndpoint } from '@cushin/api-codegen';
61
+
62
+ // Define your schemas
63
+ const UserSchema = z.object({
64
+ id: z.string(),
65
+ name: z.string(),
66
+ email: z.string().email(),
67
+ });
68
+
69
+ const CreateUserSchema = z.object({
70
+ name: z.string(),
71
+ email: z.string().email(),
72
+ });
73
+
74
+ // Define your endpoints
75
+ export const apiConfig = defineConfig({
76
+ baseUrl: 'https://api.example.com',
77
+ endpoints: {
78
+ // GET request
79
+ getUser: defineEndpoint({
80
+ path: '/users/:id',
81
+ method: 'GET',
82
+ params: z.object({ id: z.string() }),
83
+ response: UserSchema,
84
+ tags: ['users', 'query'],
85
+ description: 'Get user by ID',
86
+ }),
87
+
88
+ // GET with query params
89
+ listUsers: defineEndpoint({
90
+ path: '/users',
91
+ method: 'GET',
92
+ query: z.object({
93
+ page: z.number().optional(),
94
+ limit: z.number().optional(),
95
+ }),
96
+ response: z.array(UserSchema),
97
+ tags: ['users', 'query'],
98
+ }),
99
+
100
+ // POST request
101
+ createUser: defineEndpoint({
102
+ path: '/users',
103
+ method: 'POST',
104
+ body: CreateUserSchema,
105
+ response: UserSchema,
106
+ tags: ['users', 'mutation'],
107
+ }),
108
+
109
+ // PUT request with params
110
+ updateUser: defineEndpoint({
111
+ path: '/users/:id',
112
+ method: 'PUT',
113
+ params: z.object({ id: z.string() }),
114
+ body: CreateUserSchema,
115
+ response: UserSchema,
116
+ tags: ['users', 'mutation'],
117
+ }),
118
+
119
+ // DELETE request
120
+ deleteUser: defineEndpoint({
121
+ path: '/users/:id',
122
+ method: 'DELETE',
123
+ params: z.object({ id: z.string() }),
124
+ response: z.object({ success: z.boolean() }),
125
+ tags: ['users', 'mutation'],
126
+ }),
127
+ },
128
+ });
129
+ ```
130
+
131
+ ### 3. Generate Code
132
+
133
+ ```bash
134
+ npx api-codegen generate
135
+ ```
136
+
137
+ This generates:
138
+ - `generated/types.ts` - Type definitions
139
+ - `generated/client.ts` - API client
140
+ - `generated/hooks.ts` - React Query hooks
141
+ - `generated/actions.ts` - Server Actions (Next.js only)
142
+ - `generated/queries.ts` - Server Queries (Next.js only)
143
+
144
+ ### 4. Initialize Client (Vite)
145
+
146
+ ```typescript
147
+ // lib/auth/provider.tsx
148
+ import { initializeAPIClient } from '@/lib/api/generated/client';
149
+
150
+ export function AuthProvider({ children }) {
151
+ useEffect(() => {
152
+ initializeAPIClient({
153
+ getTokens: () => {
154
+ const token = localStorage.getItem('access_token');
155
+ return token ? { accessToken: token } : null;
156
+ },
157
+ setTokens: (tokens) => {
158
+ localStorage.setItem('access_token', tokens.accessToken);
159
+ },
160
+ clearTokens: () => {
161
+ localStorage.removeItem('access_token');
162
+ },
163
+ onAuthError: () => {
164
+ router.push('/login');
165
+ },
166
+ onRefreshToken: async () => {
167
+ const response = await fetch('/api/auth/refresh', {
168
+ method: 'POST',
169
+ credentials: 'include',
170
+ });
171
+ const data = await response.json();
172
+ return data.accessToken;
173
+ },
174
+ });
175
+ }, []);
176
+
177
+ return <>{children}</>;
178
+ }
179
+ ```
180
+
181
+ ### 5. Use Generated Hooks
182
+
183
+ ```typescript
184
+ // components/UserList.tsx
185
+ import { useListUsers, useCreateUser, useDeleteUser } from '@/lib/api/generated/hooks';
186
+
187
+ export function UserList() {
188
+ // Query hook
189
+ const { data: users, isLoading } = useListUsers({
190
+ page: 1,
191
+ limit: 10,
192
+ });
193
+
194
+ // Mutation hooks
195
+ const createUser = useCreateUser({
196
+ onSuccess: () => {
197
+ console.log('User created!');
198
+ },
199
+ });
200
+
201
+ const deleteUser = useDeleteUser();
202
+
203
+ const handleCreate = () => {
204
+ createUser.mutate({
205
+ name: 'John Doe',
206
+ email: 'john@example.com',
207
+ });
208
+ };
209
+
210
+ const handleDelete = (id: string) => {
211
+ deleteUser.mutate({ id });
212
+ };
213
+
214
+ if (isLoading) return <div>Loading...</div>;
215
+
216
+ return (
217
+ <div>
218
+ <button onClick={handleCreate}>Create User</button>
219
+ {users?.map((user) => (
220
+ <div key={user.id}>
221
+ {user.name}
222
+ <button onClick={() => handleDelete(user.id)}>Delete</button>
223
+ </div>
224
+ ))}
225
+ </div>
226
+ );
227
+ }
228
+ ```
229
+
230
+ ## Next.js Usage
231
+
232
+ ### Server Components
233
+
234
+ ```typescript
235
+ // app/users/page.tsx
236
+ import { listUsersQuery } from '@/lib/api/generated/queries';
237
+
238
+ export default async function UsersPage() {
239
+ const users = await listUsersQuery({ page: 1, limit: 10 });
240
+
241
+ return (
242
+ <div>
243
+ {users.map((user) => (
244
+ <div key={user.id}>{user.name}</div>
245
+ ))}
246
+ </div>
247
+ );
248
+ }
249
+ ```
250
+
251
+ ### Server Actions
252
+
253
+ ```typescript
254
+ // app/users/actions.ts
255
+ 'use client';
256
+
257
+ import { createUserAction, deleteUserAction } from '@/lib/api/generated/actions';
258
+ import { useTransition } from 'react';
259
+
260
+ export function UserForm() {
261
+ const [isPending, startTransition] = useTransition();
262
+
263
+ const handleSubmit = async (formData: FormData) => {
264
+ startTransition(async () => {
265
+ const result = await createUserAction({
266
+ name: formData.get('name') as string,
267
+ email: formData.get('email') as string,
268
+ });
269
+
270
+ if (result.success) {
271
+ console.log('User created:', result.data);
272
+ } else {
273
+ console.error('Error:', result.error);
274
+ }
275
+ });
276
+ };
277
+
278
+ return <form action={handleSubmit}>...</form>;
279
+ }
280
+ ```
281
+
282
+ ## Configuration
283
+
284
+ ### Full Configuration Options
285
+
286
+ ```typescript
287
+ /** @type {import('@cushin/api-codegen').UserConfig} */
288
+ export default {
289
+ // Required: Provider type
290
+ provider: 'vite' | 'nextjs',
291
+
292
+ // Required: Path to endpoints configuration
293
+ endpoints: './lib/api/config/endpoints.ts',
294
+
295
+ // Required: Output directory
296
+ output: './lib/api/generated',
297
+
298
+ // Optional: Base URL (can also be set at runtime)
299
+ baseUrl: process.env.VITE_API_URL,
300
+
301
+ // Optional: Generation flags
302
+ generateHooks: true, // Generate React Query hooks
303
+ generateClient: true, // Generate API client
304
+ generateServerActions: true, // Next.js only
305
+ generateServerQueries: true, // Next.js only
306
+
307
+ // Optional: Advanced options
308
+ options: {
309
+ useClientDirective: true, // Add 'use client' to generated files
310
+ hookPrefix: 'use', // Prefix for hook names (e.g., useGetUser)
311
+ actionSuffix: 'Action', // Suffix for action names (e.g., createUserAction)
312
+ customImports: {
313
+ // Add custom imports to generated files
314
+ hooks: ['import { customHook } from "./custom"'],
315
+ },
316
+ },
317
+ };
318
+ ```
319
+
320
+ ## CLI Commands
321
+
322
+ ```bash
323
+ # Generate code from config
324
+ npx api-codegen generate
325
+
326
+ # Generate with specific config file
327
+ npx api-codegen generate --config ./custom.config.js
328
+
329
+ # Initialize new config
330
+ npx api-codegen init --provider nextjs
331
+
332
+ # Validate configuration
333
+ npx api-codegen validate
334
+ ```
335
+
336
+ ## Advanced Usage
337
+
338
+ ### Custom Base URL per Endpoint
339
+
340
+ ```typescript
341
+ defineEndpoint({
342
+ path: '/auth/login',
343
+ method: 'POST',
344
+ baseUrl: 'https://auth.example.com', // Override base URL
345
+ body: LoginSchema,
346
+ response: TokenSchema,
347
+ });
348
+ ```
349
+
350
+ ### Multiple Endpoints Files
351
+
352
+ ```typescript
353
+ // lib/api/config/modules/users.ts
354
+ export const userEndpoints = {
355
+ getUser: defineEndpoint({ ... }),
356
+ createUser: defineEndpoint({ ... }),
357
+ };
358
+
359
+ // lib/api/config/endpoints.ts
360
+ import { userEndpoints } from './modules/users';
361
+ import { productEndpoints } from './modules/products';
362
+
363
+ export const apiConfig = defineConfig({
364
+ baseUrl: 'https://api.example.com',
365
+ endpoints: {
366
+ ...userEndpoints,
367
+ ...productEndpoints,
368
+ },
369
+ });
370
+ ```
371
+
372
+ ### Custom Auth Logic
373
+
374
+ ```typescript
375
+ initializeAPIClient({
376
+ getTokens: () => {
377
+ // Custom token retrieval
378
+ return yourAuthStore.getTokens();
379
+ },
380
+ setTokens: (tokens) => {
381
+ // Custom token storage
382
+ yourAuthStore.setTokens(tokens);
383
+ },
384
+ clearTokens: () => {
385
+ // Custom cleanup
386
+ yourAuthStore.clearTokens();
387
+ },
388
+ onRefreshToken: async () => {
389
+ // Custom refresh logic
390
+ const newToken = await yourRefreshFunction();
391
+ return newToken;
392
+ },
393
+ onAuthError: () => {
394
+ // Custom error handling
395
+ yourRouter.push('/login');
396
+ },
397
+ });
398
+ ```
399
+
400
+ ## Type Safety
401
+
402
+ All generated code is fully typed:
403
+
404
+ ```typescript
405
+ // IntelliSense knows the exact shape
406
+ const { data } = useGetUser({ id: '123' });
407
+ // ^? { id: string; name: string; email: string; }
408
+
409
+ // TypeScript will error on invalid params
410
+ const { data } = useGetUser({ id: 123 }); // ❌ Type error
411
+ const { data } = useGetUser({ wrongParam: '123' }); // ❌ Type error
412
+
413
+ // Mutation inputs are also typed
414
+ createUser.mutate({
415
+ name: 'John',
416
+ email: 'invalid', // ❌ Type error: invalid email format
417
+ });
418
+ ```
419
+
420
+ ## Best Practices
421
+
422
+ 1. **Organize endpoints by feature/module**
423
+ 2. **Use descriptive endpoint names**
424
+ 3. **Add descriptions to endpoints for better documentation**
425
+ 4. **Use tags for query invalidation**
426
+ 5. **Define reusable schemas**
427
+ 6. **Keep baseUrl in environment variables**
428
+
429
+ ## Contributing
430
+
431
+ Contributions are welcome! Please read our contributing guide.
432
+
433
+ ## License
434
+
435
+ MIT © Le Viet Hoang
package/dist/cli.d.ts ADDED
@@ -0,0 +1 @@
1
+ #!/usr/bin/env node