@girardmedia/bootspring 1.2.0 → 2.0.3
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 +107 -14
- package/bin/bootspring.js +166 -27
- package/cli/agent.js +189 -17
- package/cli/analyze.js +499 -0
- package/cli/audit.js +557 -0
- package/cli/auth.js +495 -38
- package/cli/billing.js +302 -0
- package/cli/build.js +695 -0
- package/cli/business.js +109 -26
- package/cli/checkpoint-utils.js +168 -0
- package/cli/checkpoint.js +639 -0
- package/cli/cloud-sync.js +447 -0
- package/cli/content.js +198 -0
- package/cli/context.js +1 -1
- package/cli/deploy.js +543 -0
- package/cli/fundraise.js +112 -50
- package/cli/github-cmd.js +435 -0
- package/cli/health.js +477 -0
- package/cli/init.js +84 -13
- package/cli/legal.js +107 -95
- package/cli/log.js +2 -2
- package/cli/loop.js +976 -73
- package/cli/manager.js +711 -0
- package/cli/metrics.js +480 -0
- package/cli/monitor.js +812 -0
- package/cli/onboard.js +521 -0
- package/cli/orchestrator.js +12 -24
- package/cli/prd.js +594 -0
- package/cli/preseed-start.js +1483 -0
- package/cli/preseed.js +2302 -0
- package/cli/project.js +436 -0
- package/cli/quality.js +233 -0
- package/cli/security.js +913 -0
- package/cli/seed.js +1441 -5
- package/cli/skill.js +273 -211
- package/cli/suggest.js +989 -0
- package/cli/switch.js +453 -0
- package/cli/visualize.js +527 -0
- package/cli/watch.js +769 -0
- package/cli/workspace.js +607 -0
- package/core/analyze-workflow.js +1134 -0
- package/core/api-client.js +535 -22
- package/core/audit-workflow.js +1350 -0
- package/core/build-orchestrator.js +480 -0
- package/core/build-state.js +577 -0
- package/core/checkpoint-engine.js +408 -0
- package/core/config.js +1109 -26
- package/core/context-loader.js +21 -1
- package/core/deploy-workflow.js +836 -0
- package/core/entitlements.js +93 -22
- package/core/github-sync.js +610 -0
- package/core/index.js +8 -1
- package/core/ingest.js +1111 -0
- package/core/metrics-engine.js +768 -0
- package/core/onboard-workflow.js +1007 -0
- package/core/preseed-workflow.js +934 -0
- package/core/preseed.js +1617 -0
- package/core/project-context.js +325 -0
- package/core/project-state.js +694 -0
- package/core/r2-sync.js +583 -0
- package/core/scaffold.js +525 -7
- package/core/session.js +258 -0
- package/core/task-extractor.js +758 -0
- package/core/telemetry.js +28 -6
- package/core/tier-enforcement.js +737 -0
- package/core/utils.js +38 -14
- package/generators/questionnaire.js +15 -12
- package/generators/sections/ai.js +7 -7
- package/generators/sections/content.js +300 -0
- package/generators/sections/index.js +3 -0
- package/generators/sections/plugins.js +7 -6
- package/generators/templates/build-planning.template.js +596 -0
- package/generators/templates/content.template.js +819 -0
- package/generators/templates/index.js +2 -1
- package/hooks/git-autopilot.js +1250 -0
- package/hooks/index.js +9 -0
- package/intelligence/agent-collab.js +2057 -0
- package/intelligence/auto-suggest.js +634 -0
- package/intelligence/content-gen.js +1589 -0
- package/intelligence/cross-project.js +1647 -0
- package/intelligence/index.js +184 -0
- package/intelligence/learning/insights.json +517 -7
- package/intelligence/learning/pattern-learner.js +1008 -14
- package/intelligence/memory/decision-tracker.js +1431 -31
- package/intelligence/memory/decisions.jsonl +0 -0
- package/intelligence/orchestrator.js +2896 -1
- package/intelligence/prd.js +92 -1
- package/intelligence/recommendation-weights.json +14 -2
- package/intelligence/recommendations.js +463 -9
- package/intelligence/workflow-composer.js +1451 -0
- package/marketplace/index.d.ts +324 -0
- package/marketplace/index.js +1921 -0
- package/mcp/contracts/mcp-contract.v1.json +342 -4
- package/mcp/registry.js +680 -3
- package/mcp/response-formatter.js +23 -0
- package/mcp/tools/assist-tool.js +78 -4
- package/mcp/tools/autopilot-tool.js +408 -0
- package/mcp/tools/content-tool.js +571 -0
- package/mcp/tools/dashboard-tool.js +251 -5
- package/mcp/tools/mvp-tool.js +344 -0
- package/mcp/tools/plugin-tool.js +23 -1
- package/mcp/tools/prd-tool.js +579 -0
- package/mcp/tools/seed-tool.js +447 -0
- package/mcp/tools/skill-tool.js +43 -14
- package/mcp/tools/suggest-tool.js +147 -0
- package/package.json +15 -6
- package/agents/README.md +0 -93
- package/agents/ai-integration-expert/context.md +0 -386
- package/agents/api-expert/context.md +0 -416
- package/agents/architecture-expert/context.md +0 -454
- package/agents/auth-expert/context.md +0 -399
- package/agents/backend-expert/context.md +0 -483
- package/agents/business-strategy-expert/context.md +0 -180
- package/agents/code-review-expert/context.md +0 -365
- package/agents/competitive-analysis-expert/context.md +0 -239
- package/agents/data-modeling-expert/context.md +0 -352
- package/agents/database-expert/context.md +0 -250
- package/agents/devops-expert/context.md +0 -446
- package/agents/email-expert/context.md +0 -379
- package/agents/financial-expert/context.md +0 -213
- package/agents/frontend-expert/context.md +0 -364
- package/agents/fundraising-expert/context.md +0 -257
- package/agents/growth-expert/context.md +0 -249
- package/agents/index.js +0 -140
- package/agents/investor-relations-expert/context.md +0 -266
- package/agents/legal-expert/context.md +0 -284
- package/agents/marketing-expert/context.md +0 -236
- package/agents/monitoring-expert/context.md +0 -362
- package/agents/operations-expert/context.md +0 -279
- package/agents/partnerships-expert/context.md +0 -286
- package/agents/payment-expert/context.md +0 -340
- package/agents/performance-expert/context.md +0 -377
- package/agents/private-equity-expert/context.md +0 -246
- package/agents/railway-expert/context.md +0 -284
- package/agents/research-expert/context.md +0 -245
- package/agents/sales-expert/context.md +0 -241
- package/agents/security-expert/context.md +0 -343
- package/agents/testing-expert/context.md +0 -414
- package/agents/ui-ux-expert/context.md +0 -448
- package/agents/vercel-expert/context.md +0 -426
- package/skills/index.js +0 -787
- package/skills/patterns/README.md +0 -163
- package/skills/patterns/ai/agents.md +0 -281
- package/skills/patterns/ai/claude.md +0 -138
- package/skills/patterns/ai/embeddings.md +0 -150
- package/skills/patterns/ai/rag.md +0 -266
- package/skills/patterns/ai/streaming.md +0 -170
- package/skills/patterns/ai/structured-output.md +0 -162
- package/skills/patterns/ai/tools.md +0 -154
- package/skills/patterns/analytics/tracking.md +0 -220
- package/skills/patterns/api/errors.md +0 -296
- package/skills/patterns/api/graphql.md +0 -440
- package/skills/patterns/api/middleware.md +0 -279
- package/skills/patterns/api/openapi.md +0 -285
- package/skills/patterns/api/rate-limiting.md +0 -231
- package/skills/patterns/api/route-handler.md +0 -217
- package/skills/patterns/api/server-action.md +0 -249
- package/skills/patterns/api/versioning.md +0 -443
- package/skills/patterns/api/webhooks.md +0 -247
- package/skills/patterns/auth/clerk.md +0 -132
- package/skills/patterns/auth/mfa.md +0 -313
- package/skills/patterns/auth/nextauth.md +0 -140
- package/skills/patterns/auth/oauth.md +0 -237
- package/skills/patterns/auth/rbac.md +0 -152
- package/skills/patterns/auth/session-management.md +0 -367
- package/skills/patterns/auth/session.md +0 -120
- package/skills/patterns/database/audit.md +0 -177
- package/skills/patterns/database/migrations.md +0 -177
- package/skills/patterns/database/pagination.md +0 -230
- package/skills/patterns/database/pooling.md +0 -357
- package/skills/patterns/database/prisma.md +0 -180
- package/skills/patterns/database/relations.md +0 -187
- package/skills/patterns/database/seeding.md +0 -246
- package/skills/patterns/database/soft-delete.md +0 -153
- package/skills/patterns/database/transactions.md +0 -162
- package/skills/patterns/deployment/ci-cd.md +0 -231
- package/skills/patterns/deployment/docker.md +0 -188
- package/skills/patterns/deployment/monitoring.md +0 -387
- package/skills/patterns/deployment/vercel.md +0 -160
- package/skills/patterns/email/resend.md +0 -143
- package/skills/patterns/email/templates.md +0 -245
- package/skills/patterns/email/transactional.md +0 -503
- package/skills/patterns/email/verification.md +0 -176
- package/skills/patterns/files/download.md +0 -243
- package/skills/patterns/files/upload.md +0 -239
- package/skills/patterns/i18n/nextintl.md +0 -188
- package/skills/patterns/logging/structured.md +0 -292
- package/skills/patterns/notifications/email-queue.md +0 -248
- package/skills/patterns/notifications/push.md +0 -279
- package/skills/patterns/payments/checkout.md +0 -303
- package/skills/patterns/payments/invoices.md +0 -287
- package/skills/patterns/payments/portal.md +0 -245
- package/skills/patterns/payments/stripe.md +0 -272
- package/skills/patterns/payments/subscriptions.md +0 -300
- package/skills/patterns/payments/usage.md +0 -279
- package/skills/patterns/performance/caching.md +0 -276
- package/skills/patterns/performance/code-splitting.md +0 -233
- package/skills/patterns/performance/edge.md +0 -254
- package/skills/patterns/performance/isr.md +0 -266
- package/skills/patterns/performance/lazy-loading.md +0 -281
- package/skills/patterns/realtime/sse.md +0 -327
- package/skills/patterns/realtime/websockets.md +0 -336
- package/skills/patterns/search/filtering.md +0 -329
- package/skills/patterns/search/fulltext.md +0 -260
- package/skills/patterns/security/audit-logging.md +0 -444
- package/skills/patterns/security/csrf.md +0 -234
- package/skills/patterns/security/headers.md +0 -252
- package/skills/patterns/security/sanitization.md +0 -258
- package/skills/patterns/security/secrets.md +0 -261
- package/skills/patterns/security/validation.md +0 -268
- package/skills/patterns/security/xss.md +0 -229
- package/skills/patterns/seo/metadata.md +0 -252
- package/skills/patterns/state/context.md +0 -349
- package/skills/patterns/state/react-query.md +0 -313
- package/skills/patterns/state/url-state.md +0 -482
- package/skills/patterns/state/zustand.md +0 -262
- package/skills/patterns/testing/api.md +0 -259
- package/skills/patterns/testing/component.md +0 -233
- package/skills/patterns/testing/coverage.md +0 -207
- package/skills/patterns/testing/fixtures.md +0 -225
- package/skills/patterns/testing/integration.md +0 -436
- package/skills/patterns/testing/mocking.md +0 -177
- package/skills/patterns/testing/playwright.md +0 -162
- package/skills/patterns/testing/snapshot.md +0 -175
- package/skills/patterns/testing/vitest.md +0 -307
- package/skills/patterns/ui/accordions.md +0 -395
- package/skills/patterns/ui/cards.md +0 -299
- package/skills/patterns/ui/dropdowns.md +0 -476
- package/skills/patterns/ui/empty-states.md +0 -320
- package/skills/patterns/ui/forms.md +0 -405
- package/skills/patterns/ui/inputs.md +0 -319
- package/skills/patterns/ui/layouts.md +0 -282
- package/skills/patterns/ui/loading.md +0 -291
- package/skills/patterns/ui/modals.md +0 -338
- package/skills/patterns/ui/navigation.md +0 -374
- package/skills/patterns/ui/tables.md +0 -407
- package/skills/patterns/ui/toasts.md +0 -300
- package/skills/patterns/ui/tooltips.md +0 -396
- package/skills/patterns/utils/dates.md +0 -435
- package/skills/patterns/utils/errors.md +0 -451
- package/skills/patterns/utils/formatting.md +0 -345
- package/skills/patterns/utils/validation.md +0 -434
- package/templates/bootspring.config.js +0 -83
- package/templates/business/business-model-canvas.md +0 -246
- package/templates/business/business-plan.md +0 -266
- package/templates/business/competitive-analysis.md +0 -312
- package/templates/fundraising/data-room-checklist.md +0 -300
- package/templates/fundraising/investor-research.md +0 -243
- package/templates/fundraising/pitch-deck-outline.md +0 -253
- package/templates/legal/gdpr-checklist.md +0 -339
- package/templates/legal/privacy-policy.md +0 -285
- package/templates/legal/terms-of-service.md +0 -222
- package/templates/mcp.json +0 -9
|
@@ -1,279 +0,0 @@
|
|
|
1
|
-
# API Middleware Patterns
|
|
2
|
-
|
|
3
|
-
Patterns for Next.js API middleware.
|
|
4
|
-
|
|
5
|
-
## Authentication Middleware
|
|
6
|
-
|
|
7
|
-
```typescript
|
|
8
|
-
// middleware.ts
|
|
9
|
-
import { NextResponse } from 'next/server'
|
|
10
|
-
import type { NextRequest } from 'next/server'
|
|
11
|
-
import { getToken } from 'next-auth/jwt'
|
|
12
|
-
|
|
13
|
-
const protectedPaths = ['/api/users', '/api/teams', '/api/billing']
|
|
14
|
-
const publicPaths = ['/api/auth', '/api/health', '/api/webhooks']
|
|
15
|
-
|
|
16
|
-
export async function middleware(request: NextRequest) {
|
|
17
|
-
const path = request.nextUrl.pathname
|
|
18
|
-
|
|
19
|
-
// Skip public paths
|
|
20
|
-
if (publicPaths.some(p => path.startsWith(p))) {
|
|
21
|
-
return NextResponse.next()
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
// Check auth for protected paths
|
|
25
|
-
if (protectedPaths.some(p => path.startsWith(p))) {
|
|
26
|
-
const token = await getToken({ req: request })
|
|
27
|
-
|
|
28
|
-
if (!token) {
|
|
29
|
-
return NextResponse.json(
|
|
30
|
-
{ error: { message: 'Unauthorized', code: 'UNAUTHORIZED' } },
|
|
31
|
-
{ status: 401 }
|
|
32
|
-
)
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
// Add user info to headers for route handlers
|
|
36
|
-
const requestHeaders = new Headers(request.headers)
|
|
37
|
-
requestHeaders.set('x-user-id', token.sub!)
|
|
38
|
-
requestHeaders.set('x-user-role', token.role as string)
|
|
39
|
-
|
|
40
|
-
return NextResponse.next({
|
|
41
|
-
request: { headers: requestHeaders }
|
|
42
|
-
})
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
return NextResponse.next()
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
export const config = {
|
|
49
|
-
matcher: '/api/:path*'
|
|
50
|
-
}
|
|
51
|
-
```
|
|
52
|
-
|
|
53
|
-
## CORS Middleware
|
|
54
|
-
|
|
55
|
-
```typescript
|
|
56
|
-
// middleware.ts
|
|
57
|
-
const allowedOrigins = [
|
|
58
|
-
'https://app.example.com',
|
|
59
|
-
'https://admin.example.com'
|
|
60
|
-
]
|
|
61
|
-
|
|
62
|
-
function corsMiddleware(request: NextRequest) {
|
|
63
|
-
const origin = request.headers.get('origin')
|
|
64
|
-
const isAllowed = origin && allowedOrigins.includes(origin)
|
|
65
|
-
|
|
66
|
-
// Handle preflight
|
|
67
|
-
if (request.method === 'OPTIONS') {
|
|
68
|
-
return new NextResponse(null, {
|
|
69
|
-
status: 204,
|
|
70
|
-
headers: {
|
|
71
|
-
'Access-Control-Allow-Origin': isAllowed ? origin : '',
|
|
72
|
-
'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, OPTIONS',
|
|
73
|
-
'Access-Control-Allow-Headers': 'Content-Type, Authorization',
|
|
74
|
-
'Access-Control-Max-Age': '86400'
|
|
75
|
-
}
|
|
76
|
-
})
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
const response = NextResponse.next()
|
|
80
|
-
|
|
81
|
-
if (isAllowed) {
|
|
82
|
-
response.headers.set('Access-Control-Allow-Origin', origin)
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
return response
|
|
86
|
-
}
|
|
87
|
-
```
|
|
88
|
-
|
|
89
|
-
## Request Logging Middleware
|
|
90
|
-
|
|
91
|
-
```typescript
|
|
92
|
-
// middleware.ts
|
|
93
|
-
import { NextResponse } from 'next/server'
|
|
94
|
-
import type { NextRequest } from 'next/server'
|
|
95
|
-
|
|
96
|
-
export async function middleware(request: NextRequest) {
|
|
97
|
-
const requestId = crypto.randomUUID()
|
|
98
|
-
const start = Date.now()
|
|
99
|
-
|
|
100
|
-
// Add request ID to headers
|
|
101
|
-
const requestHeaders = new Headers(request.headers)
|
|
102
|
-
requestHeaders.set('x-request-id', requestId)
|
|
103
|
-
|
|
104
|
-
const response = NextResponse.next({
|
|
105
|
-
request: { headers: requestHeaders }
|
|
106
|
-
})
|
|
107
|
-
|
|
108
|
-
// Log request
|
|
109
|
-
const duration = Date.now() - start
|
|
110
|
-
console.log(JSON.stringify({
|
|
111
|
-
requestId,
|
|
112
|
-
method: request.method,
|
|
113
|
-
path: request.nextUrl.pathname,
|
|
114
|
-
duration,
|
|
115
|
-
timestamp: new Date().toISOString()
|
|
116
|
-
}))
|
|
117
|
-
|
|
118
|
-
response.headers.set('x-request-id', requestId)
|
|
119
|
-
return response
|
|
120
|
-
}
|
|
121
|
-
```
|
|
122
|
-
|
|
123
|
-
## Composed Middleware
|
|
124
|
-
|
|
125
|
-
```typescript
|
|
126
|
-
// middleware.ts
|
|
127
|
-
import { NextResponse } from 'next/server'
|
|
128
|
-
import type { NextRequest, NextMiddleware } from 'next/server'
|
|
129
|
-
|
|
130
|
-
type MiddlewareFunction = (
|
|
131
|
-
request: NextRequest
|
|
132
|
-
) => Promise<NextResponse | null> | NextResponse | null
|
|
133
|
-
|
|
134
|
-
// Compose multiple middlewares
|
|
135
|
-
function composeMiddleware(...middlewares: MiddlewareFunction[]) {
|
|
136
|
-
return async (request: NextRequest) => {
|
|
137
|
-
for (const middleware of middlewares) {
|
|
138
|
-
const result = await middleware(request)
|
|
139
|
-
// If middleware returns a response, stop chain
|
|
140
|
-
if (result) return result
|
|
141
|
-
}
|
|
142
|
-
return NextResponse.next()
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
// Individual middlewares
|
|
147
|
-
async function authMiddleware(request: NextRequest) {
|
|
148
|
-
if (request.nextUrl.pathname.startsWith('/api/protected')) {
|
|
149
|
-
const token = await getToken({ req: request })
|
|
150
|
-
if (!token) {
|
|
151
|
-
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
return null // Continue to next middleware
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
function loggingMiddleware(request: NextRequest) {
|
|
158
|
-
console.log(`${request.method} ${request.nextUrl.pathname}`)
|
|
159
|
-
return null
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
async function rateLimitMiddleware(request: NextRequest) {
|
|
163
|
-
const ip = request.ip ?? 'unknown'
|
|
164
|
-
const result = await checkRateLimit(ip)
|
|
165
|
-
if (!result.success) {
|
|
166
|
-
return NextResponse.json({ error: 'Rate limited' }, { status: 429 })
|
|
167
|
-
}
|
|
168
|
-
return null
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
// Export composed middleware
|
|
172
|
-
export const middleware = composeMiddleware(
|
|
173
|
-
loggingMiddleware,
|
|
174
|
-
rateLimitMiddleware,
|
|
175
|
-
authMiddleware
|
|
176
|
-
)
|
|
177
|
-
|
|
178
|
-
export const config = {
|
|
179
|
-
matcher: '/api/:path*'
|
|
180
|
-
}
|
|
181
|
-
```
|
|
182
|
-
|
|
183
|
-
## API Key Middleware
|
|
184
|
-
|
|
185
|
-
```typescript
|
|
186
|
-
// lib/api-key.ts
|
|
187
|
-
import { prisma } from '@/lib/db'
|
|
188
|
-
import { headers } from 'next/headers'
|
|
189
|
-
|
|
190
|
-
export async function validateApiKey() {
|
|
191
|
-
const headersList = headers()
|
|
192
|
-
const apiKey = headersList.get('x-api-key')
|
|
193
|
-
|
|
194
|
-
if (!apiKey) {
|
|
195
|
-
return null
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
const key = await prisma.apiKey.findUnique({
|
|
199
|
-
where: { key: apiKey },
|
|
200
|
-
include: { user: true }
|
|
201
|
-
})
|
|
202
|
-
|
|
203
|
-
if (!key || key.revokedAt) {
|
|
204
|
-
return null
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
// Update last used
|
|
208
|
-
await prisma.apiKey.update({
|
|
209
|
-
where: { id: key.id },
|
|
210
|
-
data: { lastUsedAt: new Date() }
|
|
211
|
-
})
|
|
212
|
-
|
|
213
|
-
return key.user
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
// Usage in route handler
|
|
217
|
-
export async function GET(request: Request) {
|
|
218
|
-
const user = await validateApiKey()
|
|
219
|
-
|
|
220
|
-
if (!user) {
|
|
221
|
-
return Response.json(
|
|
222
|
-
{ error: 'Invalid API key' },
|
|
223
|
-
{ status: 401 }
|
|
224
|
-
)
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
// Continue with authenticated user...
|
|
228
|
-
}
|
|
229
|
-
```
|
|
230
|
-
|
|
231
|
-
## Request Validation Middleware
|
|
232
|
-
|
|
233
|
-
```typescript
|
|
234
|
-
// lib/validate-request.ts
|
|
235
|
-
import { NextRequest, NextResponse } from 'next/server'
|
|
236
|
-
import { z } from 'zod'
|
|
237
|
-
|
|
238
|
-
export function withValidation<T extends z.ZodSchema>(
|
|
239
|
-
schema: T,
|
|
240
|
-
handler: (
|
|
241
|
-
request: NextRequest,
|
|
242
|
-
data: z.infer<T>
|
|
243
|
-
) => Promise<NextResponse>
|
|
244
|
-
) {
|
|
245
|
-
return async (request: NextRequest) => {
|
|
246
|
-
try {
|
|
247
|
-
const body = await request.json()
|
|
248
|
-
const data = schema.parse(body)
|
|
249
|
-
return handler(request, data)
|
|
250
|
-
} catch (error) {
|
|
251
|
-
if (error instanceof z.ZodError) {
|
|
252
|
-
return NextResponse.json(
|
|
253
|
-
{ error: { message: 'Validation failed', details: error.errors } },
|
|
254
|
-
{ status: 400 }
|
|
255
|
-
)
|
|
256
|
-
}
|
|
257
|
-
throw error
|
|
258
|
-
}
|
|
259
|
-
}
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
// Usage
|
|
263
|
-
const CreateUserSchema = z.object({
|
|
264
|
-
email: z.string().email(),
|
|
265
|
-
name: z.string().min(1)
|
|
266
|
-
})
|
|
267
|
-
|
|
268
|
-
export const POST = withValidation(CreateUserSchema, async (request, data) => {
|
|
269
|
-
const user = await createUser(data)
|
|
270
|
-
return NextResponse.json({ data: user }, { status: 201 })
|
|
271
|
-
})
|
|
272
|
-
```
|
|
273
|
-
|
|
274
|
-
## When to Use
|
|
275
|
-
|
|
276
|
-
- Authentication/authorization
|
|
277
|
-
- Request logging
|
|
278
|
-
- Rate limiting
|
|
279
|
-
- CORS handling
|
|
@@ -1,285 +0,0 @@
|
|
|
1
|
-
# OpenAPI Documentation Patterns
|
|
2
|
-
|
|
3
|
-
Patterns for API documentation with OpenAPI/Swagger.
|
|
4
|
-
|
|
5
|
-
## OpenAPI Spec Definition
|
|
6
|
-
|
|
7
|
-
```typescript
|
|
8
|
-
// lib/openapi.ts
|
|
9
|
-
import { OpenAPIRegistry, OpenApiGeneratorV3 } from '@asteasolutions/zod-to-openapi'
|
|
10
|
-
import { z } from 'zod'
|
|
11
|
-
|
|
12
|
-
export const registry = new OpenAPIRegistry()
|
|
13
|
-
|
|
14
|
-
// Define schemas
|
|
15
|
-
export const UserSchema = registry.register(
|
|
16
|
-
'User',
|
|
17
|
-
z.object({
|
|
18
|
-
id: z.string().openapi({ example: 'usr_123' }),
|
|
19
|
-
email: z.string().email().openapi({ example: 'user@example.com' }),
|
|
20
|
-
name: z.string().openapi({ example: 'John Doe' }),
|
|
21
|
-
role: z.enum(['USER', 'ADMIN']).openapi({ example: 'USER' }),
|
|
22
|
-
createdAt: z.string().datetime().openapi({ example: '2024-01-01T00:00:00Z' })
|
|
23
|
-
})
|
|
24
|
-
)
|
|
25
|
-
|
|
26
|
-
export const ErrorSchema = registry.register(
|
|
27
|
-
'Error',
|
|
28
|
-
z.object({
|
|
29
|
-
error: z.object({
|
|
30
|
-
message: z.string(),
|
|
31
|
-
code: z.string()
|
|
32
|
-
})
|
|
33
|
-
})
|
|
34
|
-
)
|
|
35
|
-
|
|
36
|
-
// Register endpoints
|
|
37
|
-
registry.registerPath({
|
|
38
|
-
method: 'get',
|
|
39
|
-
path: '/api/users/{id}',
|
|
40
|
-
tags: ['Users'],
|
|
41
|
-
summary: 'Get user by ID',
|
|
42
|
-
request: {
|
|
43
|
-
params: z.object({
|
|
44
|
-
id: z.string().openapi({ description: 'User ID' })
|
|
45
|
-
})
|
|
46
|
-
},
|
|
47
|
-
responses: {
|
|
48
|
-
200: {
|
|
49
|
-
description: 'User found',
|
|
50
|
-
content: {
|
|
51
|
-
'application/json': {
|
|
52
|
-
schema: z.object({ data: UserSchema })
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
},
|
|
56
|
-
404: {
|
|
57
|
-
description: 'User not found',
|
|
58
|
-
content: {
|
|
59
|
-
'application/json': {
|
|
60
|
-
schema: ErrorSchema
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
})
|
|
66
|
-
|
|
67
|
-
// Generate spec
|
|
68
|
-
export function generateOpenAPISpec() {
|
|
69
|
-
const generator = new OpenApiGeneratorV3(registry.definitions)
|
|
70
|
-
|
|
71
|
-
return generator.generateDocument({
|
|
72
|
-
openapi: '3.0.0',
|
|
73
|
-
info: {
|
|
74
|
-
title: 'My API',
|
|
75
|
-
version: '1.0.0',
|
|
76
|
-
description: 'API documentation'
|
|
77
|
-
},
|
|
78
|
-
servers: [
|
|
79
|
-
{ url: 'https://api.example.com', description: 'Production' },
|
|
80
|
-
{ url: 'http://localhost:3000', description: 'Development' }
|
|
81
|
-
]
|
|
82
|
-
})
|
|
83
|
-
}
|
|
84
|
-
```
|
|
85
|
-
|
|
86
|
-
## API Route for Spec
|
|
87
|
-
|
|
88
|
-
```typescript
|
|
89
|
-
// app/api/openapi/route.ts
|
|
90
|
-
import { generateOpenAPISpec } from '@/lib/openapi'
|
|
91
|
-
|
|
92
|
-
export async function GET() {
|
|
93
|
-
const spec = generateOpenAPISpec()
|
|
94
|
-
return Response.json(spec)
|
|
95
|
-
}
|
|
96
|
-
```
|
|
97
|
-
|
|
98
|
-
## Swagger UI Page
|
|
99
|
-
|
|
100
|
-
```tsx
|
|
101
|
-
// app/docs/page.tsx
|
|
102
|
-
'use client'
|
|
103
|
-
|
|
104
|
-
import SwaggerUI from 'swagger-ui-react'
|
|
105
|
-
import 'swagger-ui-react/swagger-ui.css'
|
|
106
|
-
|
|
107
|
-
export default function DocsPage() {
|
|
108
|
-
return (
|
|
109
|
-
<div className="min-h-screen">
|
|
110
|
-
<SwaggerUI url="/api/openapi" />
|
|
111
|
-
</div>
|
|
112
|
-
)
|
|
113
|
-
}
|
|
114
|
-
```
|
|
115
|
-
|
|
116
|
-
## Schema with Descriptions
|
|
117
|
-
|
|
118
|
-
```typescript
|
|
119
|
-
// lib/schemas/user.ts
|
|
120
|
-
import { z } from 'zod'
|
|
121
|
-
import { extendZodWithOpenApi } from '@asteasolutions/zod-to-openapi'
|
|
122
|
-
|
|
123
|
-
extendZodWithOpenApi(z)
|
|
124
|
-
|
|
125
|
-
export const CreateUserSchema = z.object({
|
|
126
|
-
email: z
|
|
127
|
-
.string()
|
|
128
|
-
.email()
|
|
129
|
-
.openapi({
|
|
130
|
-
description: 'User email address',
|
|
131
|
-
example: 'user@example.com'
|
|
132
|
-
}),
|
|
133
|
-
name: z
|
|
134
|
-
.string()
|
|
135
|
-
.min(1)
|
|
136
|
-
.max(100)
|
|
137
|
-
.openapi({
|
|
138
|
-
description: 'User display name',
|
|
139
|
-
example: 'John Doe'
|
|
140
|
-
}),
|
|
141
|
-
password: z
|
|
142
|
-
.string()
|
|
143
|
-
.min(8)
|
|
144
|
-
.openapi({
|
|
145
|
-
description: 'Password (min 8 characters)',
|
|
146
|
-
example: 'securepassword123'
|
|
147
|
-
})
|
|
148
|
-
}).openapi('CreateUserInput')
|
|
149
|
-
|
|
150
|
-
export const UserResponseSchema = z.object({
|
|
151
|
-
id: z.string(),
|
|
152
|
-
email: z.string().email(),
|
|
153
|
-
name: z.string(),
|
|
154
|
-
emailVerified: z.boolean(),
|
|
155
|
-
createdAt: z.string().datetime()
|
|
156
|
-
}).openapi('UserResponse')
|
|
157
|
-
```
|
|
158
|
-
|
|
159
|
-
## Register Multiple Endpoints
|
|
160
|
-
|
|
161
|
-
```typescript
|
|
162
|
-
// lib/openapi/users.ts
|
|
163
|
-
import { registry } from '../openapi'
|
|
164
|
-
import { CreateUserSchema, UserResponseSchema } from '../schemas/user'
|
|
165
|
-
|
|
166
|
-
// List users
|
|
167
|
-
registry.registerPath({
|
|
168
|
-
method: 'get',
|
|
169
|
-
path: '/api/users',
|
|
170
|
-
tags: ['Users'],
|
|
171
|
-
summary: 'List all users',
|
|
172
|
-
request: {
|
|
173
|
-
query: z.object({
|
|
174
|
-
page: z.string().optional().openapi({ example: '1' }),
|
|
175
|
-
limit: z.string().optional().openapi({ example: '10' })
|
|
176
|
-
})
|
|
177
|
-
},
|
|
178
|
-
responses: {
|
|
179
|
-
200: {
|
|
180
|
-
description: 'List of users',
|
|
181
|
-
content: {
|
|
182
|
-
'application/json': {
|
|
183
|
-
schema: z.object({
|
|
184
|
-
data: z.array(UserResponseSchema),
|
|
185
|
-
pagination: z.object({
|
|
186
|
-
page: z.number(),
|
|
187
|
-
limit: z.number(),
|
|
188
|
-
total: z.number()
|
|
189
|
-
})
|
|
190
|
-
})
|
|
191
|
-
}
|
|
192
|
-
}
|
|
193
|
-
}
|
|
194
|
-
}
|
|
195
|
-
})
|
|
196
|
-
|
|
197
|
-
// Create user
|
|
198
|
-
registry.registerPath({
|
|
199
|
-
method: 'post',
|
|
200
|
-
path: '/api/users',
|
|
201
|
-
tags: ['Users'],
|
|
202
|
-
summary: 'Create a new user',
|
|
203
|
-
request: {
|
|
204
|
-
body: {
|
|
205
|
-
content: {
|
|
206
|
-
'application/json': {
|
|
207
|
-
schema: CreateUserSchema
|
|
208
|
-
}
|
|
209
|
-
}
|
|
210
|
-
}
|
|
211
|
-
},
|
|
212
|
-
responses: {
|
|
213
|
-
201: {
|
|
214
|
-
description: 'User created',
|
|
215
|
-
content: {
|
|
216
|
-
'application/json': {
|
|
217
|
-
schema: z.object({ data: UserResponseSchema })
|
|
218
|
-
}
|
|
219
|
-
}
|
|
220
|
-
},
|
|
221
|
-
400: {
|
|
222
|
-
description: 'Validation error',
|
|
223
|
-
content: {
|
|
224
|
-
'application/json': {
|
|
225
|
-
schema: ErrorSchema
|
|
226
|
-
}
|
|
227
|
-
}
|
|
228
|
-
}
|
|
229
|
-
}
|
|
230
|
-
})
|
|
231
|
-
```
|
|
232
|
-
|
|
233
|
-
## Authentication in Spec
|
|
234
|
-
|
|
235
|
-
```typescript
|
|
236
|
-
// lib/openapi.ts
|
|
237
|
-
registry.registerComponent('securitySchemes', 'bearerAuth', {
|
|
238
|
-
type: 'http',
|
|
239
|
-
scheme: 'bearer',
|
|
240
|
-
bearerFormat: 'JWT'
|
|
241
|
-
})
|
|
242
|
-
|
|
243
|
-
registry.registerComponent('securitySchemes', 'apiKey', {
|
|
244
|
-
type: 'apiKey',
|
|
245
|
-
in: 'header',
|
|
246
|
-
name: 'X-API-Key'
|
|
247
|
-
})
|
|
248
|
-
|
|
249
|
-
// Apply to endpoint
|
|
250
|
-
registry.registerPath({
|
|
251
|
-
method: 'get',
|
|
252
|
-
path: '/api/protected',
|
|
253
|
-
security: [{ bearerAuth: [] }],
|
|
254
|
-
// ...
|
|
255
|
-
})
|
|
256
|
-
```
|
|
257
|
-
|
|
258
|
-
## Generate Static Spec
|
|
259
|
-
|
|
260
|
-
```typescript
|
|
261
|
-
// scripts/generate-openapi.ts
|
|
262
|
-
import fs from 'fs'
|
|
263
|
-
import { generateOpenAPISpec } from '@/lib/openapi'
|
|
264
|
-
|
|
265
|
-
// Import all endpoint registrations
|
|
266
|
-
import '@/lib/openapi/users'
|
|
267
|
-
import '@/lib/openapi/teams'
|
|
268
|
-
import '@/lib/openapi/billing'
|
|
269
|
-
|
|
270
|
-
const spec = generateOpenAPISpec()
|
|
271
|
-
|
|
272
|
-
fs.writeFileSync(
|
|
273
|
-
'public/openapi.json',
|
|
274
|
-
JSON.stringify(spec, null, 2)
|
|
275
|
-
)
|
|
276
|
-
|
|
277
|
-
console.log('OpenAPI spec generated!')
|
|
278
|
-
```
|
|
279
|
-
|
|
280
|
-
## When to Use
|
|
281
|
-
|
|
282
|
-
- Public APIs
|
|
283
|
-
- Developer documentation
|
|
284
|
-
- API clients generation
|
|
285
|
-
- Contract testing
|