@igniter-js/cli 0.1.0 → 0.1.2

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.
@@ -0,0 +1,753 @@
1
+ ---
2
+ title: https://www.npmjs.com/package/@igniter-js/core?activeTab=readme
3
+ url: https://www.npmjs.com/package/@igniter-js/core?activeTab=readme
4
+ timestamp: 2025-02-26T18:35:32.625Z
5
+ ---
6
+ ```markdown
7
+ # Igniter
8
+
9
+ Igniter is a modern, type-safe HTTP framework designed to streamline the development of scalable TypeScript applications. It combines the flexibility of traditional HTTP frameworks with the power of full-stack type safety, making it the ideal choice for teams building robust web applications.
10
+
11
+ ## Why Igniter?
12
+
13
+ * **Type Safety Without Compromise**: End-to-end type safety from your API routes to your client code, catching errors before they reach production
14
+ * **Framework Agnostic**: Seamlessly integrates with Next.js, Express, Fastify, or any Node.js framework
15
+ * **Developer Experience First**: Built with TypeScript best practices and modern development patterns in mind
16
+ * **Production Ready**: Being used in production by companies of all sizes
17
+ * **Minimal Boilerplate**: Get started quickly without sacrificing scalability
18
+ * **Flexible Architecture**: Adapts to your project's needs, from small APIs to large-scale applications
19
+
20
+ ## Features
21
+
22
+ * 🎯 Full TypeScript Support: End-to-end type safety from your API routes to your client code
23
+ * 🚀 Modern Architecture: Built with modern TypeScript features and best practices
24
+ * 🔒 Type-Safe Routing: Route parameters and query strings are fully typed
25
+ * 🔌 Middleware System: Powerful and flexible middleware support with full type inference
26
+ * 🎭 Context Sharing: Share context between middlewares and route handlers
27
+ * 🔄 Built-in Error Handling: Comprehensive error handling with type-safe error responses
28
+ * 🍪 Cookie Management: Built-in cookie handling with signing support
29
+ * 📦 Framework Agnostic: Works with any Node.js framework (Express, Fastify, Next.js, etc.)
30
+
31
+ ## Getting Started
32
+
33
+ ### Installation
34
+
35
+ ```bash
36
+ npm install @igniter-js/core
37
+ ```
38
+
39
+ ```bash
40
+ # or
41
+ yarn add @igniter-js/core
42
+ ```
43
+
44
+ ```bash
45
+ # or
46
+ pnpm add @igniter-js/core
47
+ ```
48
+
49
+ ```bash
50
+ # or
51
+ bun add @igniter-js/core
52
+ ```
53
+
54
+ ### Quick Start Guide
55
+
56
+ Building an API with Igniter is straightforward and intuitive. Here's how to get started:
57
+
58
+ ### Project Structure
59
+
60
+ Igniter promotes a feature-based architecture that scales with your application:
61
+
62
+ ```
63
+ src/
64
+ ├── igniter.ts # Core initialization
65
+ ├── igniter.client.ts # Client implementation
66
+ ├── igniter.context.ts # Context management
67
+ ├── igniter.router.ts # Router configuration
68
+ ├── features/ # Application features
69
+ │ └── [feature]/
70
+ │ ├── presentation/ # Feature presentation layer
71
+ │ │ ├── components/ # Feature-specific components
72
+ │ │ ├── hooks/ # Custom hooks
73
+ │ │ ├── contexts/ # Feature contexts
74
+ │ │ └── utils/ # Utility functions
75
+ │ ├── controllers/ # Feature controllers
76
+ │ │ └── [feature].controller.ts
77
+ │ ├── procedures/ # Feature procedures/middleware
78
+ │ │ └── [feature].procedure.ts
79
+ │ ├── [feature].interfaces.ts # Type definitions(interfaces, entities, inputs and outputs)
80
+ │ └── index.ts # Feature exports
81
+ ```
82
+
83
+ ### Understanding the Structure
84
+
85
+ * Feature-based Organization: Each feature is self-contained with its own controllers, procedures, and types
86
+ * Clear Separation of Concerns: Presentation, business logic, and data access are clearly separated
87
+ * Scalable Architecture: Easy to add new features without affecting existing ones
88
+ * Maintainable Codebase: Consistent structure makes it easy for teams to navigate and maintain
89
+
90
+ 1. Initialize Igniter
91
+
92
+ ```typescript
93
+ // src/igniter.ts
94
+
95
+ import { Igniter } from "@igniter-js/core";
96
+ import type { IgniterAppContext } from "./igniter.context";
97
+
98
+ /**
99
+ * @description Initialize the Igniter Router
100
+ * @see https://igniter.felipebarcelospro.github.io/docs/getting-started/installation
101
+ */
102
+ export const igniter = Igniter.context<IgniterAppContext>().create()
103
+ ```
104
+
105
+ 2. Define your App Global Context
106
+
107
+ ```typescript
108
+ // src/igniter.context
109
+ import { prisma } from "@/lib/db";
110
+ import { Invariant } from "@/utils";
111
+
112
+ /**
113
+ * @description Create the context of the application
114
+ * @see https://igniter.felipebarcelospro.github.io/docs/getting-started/installation
115
+ */
116
+ export const createIgniterAppContext = () => {
117
+ return {
118
+ providers: {
119
+ database: prisma,
120
+ rules: Invariant.initialize('Igniter')
121
+ }
122
+ }
123
+ }
124
+
125
+ /**
126
+ * @description The context of the application
127
+ * @see https://igniter.felipebarcelospro.github.io/docs/getting-started/installation
128
+ */
129
+ export type IgniterAppContext = Awaited<ReturnType<typeof createIgniterAppContext>>;
130
+ ```
131
+
132
+ 3. Create your first controller
133
+
134
+ ```typescript
135
+ // src/features/user/controllers/user.controller.ts
136
+ import { igniter } from '@/igniter'
137
+
138
+ export const userController = igniter.controller({
139
+ path: '/users',
140
+ actions: {
141
+ // Query action (GET)
142
+ list: igniter.query({
143
+ path: '/',
144
+ use: [auth()],
145
+ query: z.object({
146
+ page: z.number().optional(),
147
+ limit: z.number().optional()
148
+ }),
149
+ handler: async (ctx) => {
150
+ return ctx.response.success({
151
+ users: [
152
+ { id: 1, name: 'John Doe' }
153
+ ]
154
+ })
155
+ }
156
+ }),
157
+
158
+ // Mutation action (POST)
159
+ create: igniter.mutation({
160
+ path: '/',
161
+ method: 'POST',
162
+ use: [auth()],
163
+ body: z.object({
164
+ name: z.string(),
165
+ email: z.string().email()
166
+ }),
167
+ handler: async (ctx) => {
168
+ const { name, email } = ctx.request.body
169
+
170
+ return ctx.response.created({
171
+ id: '1',
172
+ name,
173
+ email
174
+ })
175
+ }
176
+ })
177
+ }
178
+ })
179
+ ```
180
+
181
+ 4. Initialize Igniter Router with your framework
182
+
183
+ ```typescript
184
+ // src/igniter.router.ts
185
+ import { igniter } from '@/igniter'
186
+ import { userController } from '@/features/user'
187
+
188
+ export const AppRouter = igniter.router({
189
+ baseURL: 'http://localhost:3000',
190
+ basePATH: '/api/v1',
191
+ controllers: {
192
+ users: userController
193
+ }
194
+ })
195
+
196
+ // Use with any HTTP framework
197
+ // Example with Express:
198
+ import { AppRouter } from '@/igniter.router'
199
+
200
+ app.use(async (req, res) => {
201
+ const response = await AppRouter.handler(req)
202
+ res.status(response.status).json(response)
203
+ })
204
+
205
+ // Example with Bun:
206
+ import { AppRouter } from '@/igniter.router'
207
+
208
+ Bun.serve({
209
+ fetch: AppRouter.handler
210
+ })
211
+
212
+ // Example with Next Route Handlers:
213
+ // src/app/api/v1/[[...all]]/route.ts
214
+ import { AppRouter } from '@/igniter.router'
215
+ import { nextRouteHandlerAdapter } from '@igniter-js/core/adapters/next'
216
+
217
+ export const { GET, POST, PUT, DELETE } = nextRouteHandlerAdapter(AppRouter)
218
+ ```
219
+
220
+ ## Core Concepts
221
+
222
+ ### Application Context
223
+
224
+ The context system is the backbone of your application:
225
+
226
+ ```typescript
227
+ type AppContext = {
228
+ db: Database
229
+ user?: User
230
+ }
231
+
232
+ const igniter = Igniter.context<AppContext>().create()
233
+ ```
234
+
235
+ #### Best Practices for Context
236
+
237
+ * Keep context focused and specific to your application needs
238
+ * Use TypeScript interfaces to define context shape
239
+ * Consider splitting large contexts into domain-specific contexts
240
+ * Avoid storing request-specific data in global context
241
+
242
+ ### Procedures (Middleware)
243
+
244
+ Procedures provide a powerful way to handle cross-cutting concerns:
245
+
246
+ ```typescript
247
+ import { igniter } from '@/igniter'
248
+
249
+ const auth = igniter.procedure({
250
+ handler: async (_, ctx) => {
251
+ const token = ctx.request.headers.get('authorization')
252
+ if (!token) {
253
+ return ctx.response.unauthorized()
254
+ }
255
+
256
+ const user = await verifyToken(token)
257
+ return { user }
258
+ }
259
+ })
260
+
261
+ // Use in actions
262
+ const protectedAction = igniter.query({
263
+ path: '/protected',
264
+ use: [auth()],
265
+ handler: (ctx) => {
266
+ // ctx.context.user is typed!
267
+ return ctx.response.success({ user: ctx.context.user })
268
+ }
269
+ })
270
+ ```
271
+
272
+ #### Common Use Cases for Procedures
273
+
274
+ * Authentication and Authorization
275
+ * Request Validation
276
+ * Logging and Monitoring
277
+ * Error Handling
278
+ * Performance Tracking
279
+ * Data Transformation
280
+
281
+ ### Controllers and Actions
282
+
283
+ Controllers organize related functionality:
284
+
285
+ ```typescript
286
+ import { igniter } from '@/igniter'
287
+
288
+ const userController = igniter.controller({
289
+ path: 'users',
290
+ actions: {
291
+ list: igniter.query({
292
+ path: '/',
293
+ handler: (ctx) => ctx.response.success({ users: [] })
294
+ }),
295
+
296
+ get: igniter.query({
297
+ path: '/:id',
298
+ handler: (ctx) => {
299
+ // ctx.request.params.id is typed!
300
+ return ctx.response.success({ user: { id: ctx.request.params.id } })
301
+ }
302
+ })
303
+ }
304
+ })
305
+ ```
306
+
307
+ #### Controller Best Practices
308
+
309
+ * Group related actions together
310
+ * Keep controllers focused on a single resource or domain
311
+ * Use meaningful names that reflect the resource
312
+ * Implement proper error handling
313
+ * Follow RESTful conventions where appropriate
314
+
315
+ ### Type-Safe Responses
316
+
317
+ Igniter provides a robust response system:
318
+
319
+ ```typescript
320
+ handler: async (ctx) => {
321
+ // Success responses
322
+ ctx.response.success({ data: 'ok' })
323
+ ctx.response.created({ id: 1 })
324
+ ctx.response.noContent()
325
+
326
+ // Error responses
327
+ ctx.response.badRequest('Invalid input')
328
+ ctx.response.unauthorized()
329
+ ctx.response.forbidden('Access denied')
330
+ ctx.response.notFound('Resource not found')
331
+
332
+ // Custom responses
333
+ ctx.response.status(418).setHeader('X-Custom', 'value').json({ message: "I'm a teapot" })
334
+ }
335
+ ```
336
+
337
+ ### Cookie Management
338
+
339
+ Secure cookie handling made easy:
340
+
341
+ ```typescript
342
+ handler: async (ctx) => {
343
+ // Set cookies
344
+ await ctx.response.setCookie('session', 'value', {
345
+ httpOnly: true,
346
+ secure: true,
347
+ sameSite: 'strict'
348
+ })
349
+
350
+ // Set signed cookies
351
+ await ctx.response.setSignedCookie('token', 'sensitive-data', 'secret-key')
352
+
353
+ // Get cookies
354
+ const session = ctx.request.cookies.get('session')
355
+ const token = await ctx.request.cookies.getSigned('token', 'secret-key')
356
+ }
357
+ ```
358
+
359
+ ### React Client Integration
360
+
361
+ The Igniter React client provides a seamless integration with your frontend:
362
+
363
+ #### Setup
364
+
365
+ First, create your API client:
366
+
367
+ ```typescript
368
+ // src/igniter.client.ts
369
+ import { createIgniterClient, useIgniterQueryClient } from '@igniter-js/core/client';
370
+ import { AppRouter } from './igniter.router';
371
+
372
+ /**
373
+ * Client for Igniter
374
+ *
375
+ * This client is used to fetch data on the client-side
376
+ * It uses the createIgniterClient function to create a client instance
377
+ *
378
+ */
379
+ export const api = createIgniterClient(AppRouter);
380
+
381
+ /**
382
+ * Query client for Igniter
383
+ *
384
+ * This client provides access to the Igniter query functions
385
+ * and handles data fetching with respect to the application router.
386
+ * It will enable the necessary hooks for query management.
387
+ */
388
+ export const useQueryClient = useIgniterQueryClient<typeof AppRouter>;
389
+ ```
390
+
391
+ Then, wrap your app with the Igniter provider:
392
+
393
+ ```typescript
394
+ // app/providers.tsx
395
+ import { IgniterProvider } from '@igniter-js/core/client'
396
+
397
+ export function Providers({ children }: { children: React.ReactNode }) {
398
+ return (
399
+ <IgniterProvider>
400
+ {children}
401
+ </IgniterProvider>
402
+ )
403
+ }
404
+ ```
405
+
406
+ #### Queries
407
+
408
+ Use the `useQuery` hook for data fetching with automatic caching and revalidation:
409
+
410
+ ```typescript
411
+ import { api } from '@/igniter.client'
412
+
413
+ function UsersList() {
414
+ const listUsers = api.users.list.useQuery({
415
+ // Optional configuration
416
+ initialData: [], // Initial data while loading
417
+ staleTime: 1000 * 60, // Data stays fresh for 1 minute
418
+ refetchInterval: 1000 * 30, // Refetch every 30 seconds
419
+ refetchOnWindowFocus: true, // Refetch when window regains focus
420
+ refetchOnMount: true, // Refetch when component mounts
421
+ refetchOnReconnect: true, // Refetch when reconnecting
422
+ onLoading: (isLoading) => console.log('Loading:', isLoading),
423
+ onRequest: (response) => console.log('Data received:', response)
424
+ })
425
+
426
+ if (loading) return <div>Loading...</div>
427
+
428
+ return (
429
+ <div>
430
+ <button onClick={() => refetch()}>Refresh</button>
431
+ {users.map(user => (
432
+ <div key={user.id}>{user.name}</div>
433
+ ))}
434
+ </div>
435
+ )
436
+ }
437
+ ```
438
+
439
+ #### Mutations
440
+
441
+ Use the `useMutation` hook for data modifications:
442
+
443
+ ```typescript
444
+ function CreateUserForm() {
445
+ const createUser = api.users.create.useMutation({
446
+ // Optional configuration
447
+ defaultValues: { name: '', email: '' },
448
+ onLoading: (isLoading) => console.log('Loading:', isLoading),
449
+ onRequest: (response) => console.log('Created user:', response)
450
+ })
451
+
452
+ const handleSubmit = async (e: React.FormEvent) => {
453
+ e.preventDefault()
454
+ try {
455
+ await createUser.mutate({
456
+ body: {
457
+ name: 'John Doe',
458
+ email: 'john@example.com'
459
+ }
460
+ })
461
+ // Handle success
462
+ } catch (error) {
463
+ // Handle error
464
+ }
465
+ }
466
+
467
+ return (
468
+ <form onSubmit={handleSubmit}>
469
+ {/* Form fields */}
470
+ <button type="submit" disabled={createUser.loading}>
471
+ {createUser.loading ? 'Creating...' : 'Create User'}
472
+ </button>
473
+ </form>
474
+ )
475
+ }
476
+ ```
477
+
478
+ #### Cache Invalidation
479
+
480
+ Invalidate queries manually or automatically after mutations:
481
+
482
+ ```typescript
483
+ function AdminPanel() {
484
+ const queryClient = useIgniterQueryClient()
485
+
486
+ // Invalidate specific queries
487
+ const invalidateUsers = () => {
488
+ queryClient.invalidate('users.list')
489
+ }
490
+
491
+ // Invalidate multiple queries
492
+ const invalidateAll = () => {
493
+ queryClient.invalidate([
494
+ 'users.list',
495
+ 'users.get'
496
+ ])
497
+ }
498
+
499
+ return (
500
+ <button onClick={invalidateUsers}>
501
+ Refresh Users
502
+ </button>
503
+ )
504
+ }
505
+ ```
506
+
507
+ #### Automatic Type Inference
508
+
509
+ The client provides full type inference for your API:
510
+
511
+ ```typescript
512
+ // All these types are automatically inferred
513
+ type User = InferOutput<typeof api.users.get>
514
+ type CreateUserInput = InferInput<typeof api.users.create>
515
+ type QueryKeys = InferCacheKeysFromRouter<typeof router>
516
+
517
+ // TypeScript will show errors for invalid inputs
518
+ api.users.create.useMutation({
519
+ onRequest: (data) => {
520
+ data.id // ✅ Typed as string
521
+ data.invalid // ❌ TypeScript error
522
+ }
523
+ })
524
+ ```
525
+
526
+ ### Server Actions (Next.js App Router)
527
+
528
+ Use direct server calls with React Server Components:
529
+
530
+ ```typescript
531
+ // app/users/page.tsx
532
+ import { api } from '@/igniter.client'
533
+
534
+ export default async function UsersPage() {
535
+ const users = await api.users.list.call()
536
+
537
+ return (
538
+ <div>
539
+ {users.map(user => (
540
+ <div key={user.id}>{user.name}</div>
541
+ ))}
542
+ </div>
543
+ )
544
+ }
545
+ ```
546
+
547
+ Use with Server Actions:
548
+
549
+ ```typescript
550
+ // app/users/actions.ts
551
+ 'use server'
552
+
553
+ import { api } from '@/igniter.client'
554
+
555
+ export async function createUser(formData: FormData) {
556
+ const name = formData.get('name') as string
557
+ const email = formData.get('email') as string
558
+
559
+ return api.users.create.call({
560
+ body: { name, email }
561
+ })
562
+ }
563
+
564
+ // app/users/create-form.tsx
565
+ export function CreateUserForm() {
566
+ return (
567
+ <form action={createUser}>
568
+ <input name="name" />
569
+ <input name="email" type="email" />
570
+ <button type="submit">Create User</button>
571
+ </form>
572
+ )
573
+ }
574
+ ```
575
+
576
+ Combine Server and Client Components:
577
+
578
+ ```typescript
579
+ // app/users/hybrid-page.tsx
580
+ import { api } from '@/igniter.client'
581
+
582
+ // Server Component
583
+ async function UsersList() {
584
+ const users = await api.users.list.call()
585
+ return (
586
+ <div>
587
+ {users.map(user => (
588
+ <div key={user.id}>{user.name}</div>
589
+ ))}
590
+ </div>
591
+ )
592
+ }
593
+
594
+ // Client Component
595
+ 'use client'
596
+ function UserCount() {
597
+ const { count } = api.users.count.useQuery()
598
+ return <div>Total Users: {count}</div>
599
+ }
600
+
601
+ // Main Page Component
602
+ export default function UsersPage() {
603
+ return (
604
+ <div>
605
+ <UserCount />
606
+ <Suspense fallback={<div>Loading...</div>}>
607
+ <UsersList />
608
+ </Suspense>
609
+ </div>
610
+ )
611
+ }
612
+ ```
613
+
614
+ ## Performance Optimization
615
+
616
+ * Caching Strategy: Configure caching behavior per query
617
+ * Automatic Revalidation: Keep data fresh with smart revalidation
618
+ * Prefetching: Improve perceived performance
619
+ * Optimistic Updates: Provide instant feedback
620
+ * Parallel Queries: Handle multiple requests efficiently
621
+
622
+ ## Error Handling and Recovery
623
+
624
+ ```typescript
625
+ function UserProfile() {
626
+ const { data, error, retry } = api.users.get.useQuery({
627
+ onError: (error) => {
628
+ console.error('Failed to fetch user:', error)
629
+ },
630
+ retry: 3, // Retry failed requests
631
+ retryDelay: 1000, // Wait 1 second between retries
632
+ })
633
+
634
+ if (error) {
635
+ return (
636
+ <div>
637
+ Error loading profile
638
+ <button onClick={retry}>Try Again</button>
639
+ </div>
640
+ )
641
+ }
642
+
643
+ return <div>{/* ... */}</div>
644
+ }
645
+ ```
646
+
647
+ ## Advanced Usage
648
+
649
+ ### Server-Side Rendering
650
+
651
+ Use direct server calls with React Server Components:
652
+
653
+ ```typescript
654
+ // app/users/page.tsx
655
+ import { api } from '@/igniter.client'
656
+
657
+ export default async function UsersPage() {
658
+ const users = await api.users.list.call()
659
+
660
+ return (
661
+ <div>
662
+ {users.map(user => (
663
+ <div key={user.id}>{user.name}</div>
664
+ ))}
665
+ </div>
666
+ )
667
+ }
668
+ ```
669
+
670
+ ## Testing
671
+
672
+ Igniter is designed with testability in mind:
673
+
674
+ ```typescript
675
+ import { router } from '@/igniter.router'
676
+
677
+ describe('User API', () => {
678
+ it('should create a user', async () => {
679
+ const result = await router.users.create.call({
680
+ body: {
681
+ name: 'Test User',
682
+ email: 'test@example.com'
683
+ }
684
+ })
685
+
686
+ expect(result.status).toBe(201)
687
+ expect(result.data).toHaveProperty('id')
688
+ })
689
+ })
690
+ ```
691
+
692
+ ## Security Best Practices
693
+
694
+ * Use procedures for authentication and authorization
695
+ * Implement rate limiting
696
+ * Validate all inputs
697
+ * Use secure cookie options
698
+ * Handle errors safely
699
+ * Implement CORS properly
700
+
701
+ ## Performance Monitoring
702
+
703
+ ```typescript
704
+ import { igniter } from '@/igniter'
705
+
706
+ const monitor = igniter.procedure({
707
+ handler: async (_, ctx) => {
708
+ const start = performance.now()
709
+
710
+ // Wait for the next middleware/handler
711
+ const result = await ctx.next()
712
+
713
+ const duration = performance.now() - start
714
+ console.log(`${ctx.request.method} ${ctx.request.path} - ${duration}ms`)
715
+
716
+ return result
717
+ }
718
+ })
719
+ ```
720
+
721
+ ## TypeScript Configuration
722
+
723
+ Recommended tsconfig.json settings:
724
+
725
+ ```json
726
+ {
727
+ "compilerOptions": {
728
+ "target": "ES2020",
729
+ "lib": ["ES2020"],
730
+ "module": "CommonJS",
731
+ "strict": true,
732
+ "esModuleInterop": true,
733
+ "skipLibCheck": true,
734
+ "forceConsistentCasingInFileNames": true
735
+ }
736
+ }
737
+ ```
738
+
739
+ ## Contributing
740
+
741
+ We welcome contributions! Please see our [contributing guidelines](https://igniter.felipebarcelospro.github.io/docs/contributing) for details.
742
+
743
+ ## Support and Community
744
+
745
+ * 📚 [Documentation](https://igniter.felipebarcelospro.github.io/docs/)
746
+ * 💬 [Discord Community](https://discord.gg/f9366666)
747
+ * 🐛 [Issue Tracker](https://github.com/felipebarcelospro/igniter/issues)
748
+ * 🤝 [Contributing Guidelines](https://igniter.felipebarcelospro.github.io/docs/contributing)
749
+
750
+ ## License
751
+
752
+ MIT License - see the [LICENSE](https://github.com/felipebarcelospro/igniter/blob/main/LICENSE) file for details.
753
+ ```