@engjts/nexus 0.1.8 → 0.1.9

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.
Files changed (205) hide show
  1. package/package.json +1 -1
  2. package/BENCHMARK_REPORT.md +0 -343
  3. package/documentation/01-getting-started.md +0 -240
  4. package/documentation/02-context.md +0 -335
  5. package/documentation/03-routing.md +0 -397
  6. package/documentation/04-middleware.md +0 -483
  7. package/documentation/05-validation.md +0 -514
  8. package/documentation/06-error-handling.md +0 -465
  9. package/documentation/07-performance.md +0 -364
  10. package/documentation/08-adapters.md +0 -470
  11. package/documentation/09-api-reference.md +0 -548
  12. package/documentation/10-examples.md +0 -582
  13. package/documentation/11-deployment.md +0 -477
  14. package/documentation/12-sentry.md +0 -620
  15. package/documentation/13-sentry-data-storage.md +0 -996
  16. package/documentation/14-sentry-data-reference.md +0 -457
  17. package/documentation/15-sentry-summary.md +0 -409
  18. package/documentation/16-alerts-system.md +0 -745
  19. package/documentation/17-alert-adapters.md +0 -696
  20. package/documentation/18-alerts-implementation-summary.md +0 -385
  21. package/documentation/19-class-based-routing.md +0 -840
  22. package/documentation/20-websocket-realtime.md +0 -813
  23. package/documentation/21-cache-system.md +0 -510
  24. package/documentation/22-job-queue.md +0 -772
  25. package/documentation/23-sentry-plugin.md +0 -551
  26. package/documentation/24-testing-utilities.md +0 -1287
  27. package/documentation/25-api-versioning.md +0 -533
  28. package/documentation/26-context-store.md +0 -607
  29. package/documentation/27-dependency-injection.md +0 -329
  30. package/documentation/28-lifecycle-hooks.md +0 -521
  31. package/documentation/29-package-structure.md +0 -196
  32. package/documentation/30-plugin-system.md +0 -414
  33. package/documentation/31-jwt-authentication.md +0 -597
  34. package/documentation/32-cli.md +0 -268
  35. package/documentation/ALERTS-COMPLETE-SUMMARY.md +0 -429
  36. package/documentation/ALERTS-INDEX.md +0 -330
  37. package/documentation/ALERTS-QUICK-REFERENCE.md +0 -286
  38. package/documentation/README.md +0 -178
  39. package/documentation/index.html +0 -34
  40. package/modern_framework_paper.md +0 -1870
  41. package/public/css/style.css +0 -87
  42. package/public/index.html +0 -34
  43. package/public/js/app.js +0 -27
  44. package/src/advanced/cache/InMemoryCacheStore.ts +0 -68
  45. package/src/advanced/cache/MultiTierCache.ts +0 -194
  46. package/src/advanced/cache/RedisCacheStore.ts +0 -341
  47. package/src/advanced/cache/index.ts +0 -5
  48. package/src/advanced/cache/types.ts +0 -40
  49. package/src/advanced/graphql/SimpleDataLoader.ts +0 -42
  50. package/src/advanced/graphql/index.ts +0 -22
  51. package/src/advanced/graphql/server.ts +0 -252
  52. package/src/advanced/graphql/types.ts +0 -42
  53. package/src/advanced/jobs/InMemoryQueueStore.ts +0 -68
  54. package/src/advanced/jobs/JobQueue.ts +0 -556
  55. package/src/advanced/jobs/RedisQueueStore.ts +0 -367
  56. package/src/advanced/jobs/index.ts +0 -5
  57. package/src/advanced/jobs/types.ts +0 -70
  58. package/src/advanced/observability/APMManager.ts +0 -163
  59. package/src/advanced/observability/AlertManager.ts +0 -109
  60. package/src/advanced/observability/MetricRegistry.ts +0 -151
  61. package/src/advanced/observability/ObservabilityCenter.ts +0 -304
  62. package/src/advanced/observability/StructuredLogger.ts +0 -154
  63. package/src/advanced/observability/TracingManager.ts +0 -117
  64. package/src/advanced/observability/adapters.ts +0 -304
  65. package/src/advanced/observability/createObservabilityMiddleware.ts +0 -63
  66. package/src/advanced/observability/index.ts +0 -11
  67. package/src/advanced/observability/types.ts +0 -174
  68. package/src/advanced/playground/extractPathParams.ts +0 -6
  69. package/src/advanced/playground/generateFieldExample.ts +0 -31
  70. package/src/advanced/playground/generatePlaygroundHTML.ts +0 -1956
  71. package/src/advanced/playground/generateSummary.ts +0 -19
  72. package/src/advanced/playground/getTagFromPath.ts +0 -9
  73. package/src/advanced/playground/index.ts +0 -8
  74. package/src/advanced/playground/playground.ts +0 -250
  75. package/src/advanced/playground/types.ts +0 -49
  76. package/src/advanced/playground/zodToExample.ts +0 -16
  77. package/src/advanced/playground/zodToParams.ts +0 -15
  78. package/src/advanced/postman/buildAuth.ts +0 -31
  79. package/src/advanced/postman/buildBody.ts +0 -15
  80. package/src/advanced/postman/buildQueryParams.ts +0 -27
  81. package/src/advanced/postman/buildRequestItem.ts +0 -36
  82. package/src/advanced/postman/buildResponses.ts +0 -11
  83. package/src/advanced/postman/buildUrl.ts +0 -33
  84. package/src/advanced/postman/capitalize.ts +0 -4
  85. package/src/advanced/postman/generateCollection.ts +0 -59
  86. package/src/advanced/postman/generateEnvironment.ts +0 -34
  87. package/src/advanced/postman/generateExampleFromZod.ts +0 -21
  88. package/src/advanced/postman/generateFieldExample.ts +0 -45
  89. package/src/advanced/postman/generateName.ts +0 -20
  90. package/src/advanced/postman/generateUUID.ts +0 -11
  91. package/src/advanced/postman/getTagFromPath.ts +0 -10
  92. package/src/advanced/postman/index.ts +0 -28
  93. package/src/advanced/postman/postman.ts +0 -156
  94. package/src/advanced/postman/slugify.ts +0 -7
  95. package/src/advanced/postman/types.ts +0 -140
  96. package/src/advanced/realtime/index.ts +0 -18
  97. package/src/advanced/realtime/websocket.ts +0 -231
  98. package/src/advanced/sentry/index.ts +0 -1236
  99. package/src/advanced/sentry/types.ts +0 -355
  100. package/src/advanced/static/generateDirectoryListing.ts +0 -47
  101. package/src/advanced/static/generateETag.ts +0 -7
  102. package/src/advanced/static/getMimeType.ts +0 -9
  103. package/src/advanced/static/index.ts +0 -32
  104. package/src/advanced/static/isSafePath.ts +0 -13
  105. package/src/advanced/static/publicDir.ts +0 -21
  106. package/src/advanced/static/serveStatic.ts +0 -225
  107. package/src/advanced/static/spa.ts +0 -24
  108. package/src/advanced/static/types.ts +0 -159
  109. package/src/advanced/swagger/SwaggerGenerator.ts +0 -66
  110. package/src/advanced/swagger/buildOperation.ts +0 -61
  111. package/src/advanced/swagger/buildParameters.ts +0 -61
  112. package/src/advanced/swagger/buildRequestBody.ts +0 -21
  113. package/src/advanced/swagger/buildResponses.ts +0 -54
  114. package/src/advanced/swagger/capitalize.ts +0 -5
  115. package/src/advanced/swagger/convertPath.ts +0 -9
  116. package/src/advanced/swagger/createSwagger.ts +0 -12
  117. package/src/advanced/swagger/generateOperationId.ts +0 -21
  118. package/src/advanced/swagger/generateSpec.ts +0 -105
  119. package/src/advanced/swagger/generateSummary.ts +0 -24
  120. package/src/advanced/swagger/generateSwaggerUI.ts +0 -70
  121. package/src/advanced/swagger/generateThemeCss.ts +0 -53
  122. package/src/advanced/swagger/index.ts +0 -25
  123. package/src/advanced/swagger/swagger.ts +0 -237
  124. package/src/advanced/swagger/types.ts +0 -206
  125. package/src/advanced/swagger/zodFieldToOpenAPI.ts +0 -94
  126. package/src/advanced/swagger/zodSchemaToOpenAPI.ts +0 -50
  127. package/src/advanced/swagger/zodToOpenAPI.ts +0 -22
  128. package/src/advanced/testing/factory.ts +0 -509
  129. package/src/advanced/testing/harness.ts +0 -612
  130. package/src/advanced/testing/index.ts +0 -430
  131. package/src/advanced/testing/load-test.ts +0 -618
  132. package/src/advanced/testing/mock-server.ts +0 -498
  133. package/src/advanced/testing/mock.ts +0 -670
  134. package/src/cli/bin.ts +0 -9
  135. package/src/cli/cli.ts +0 -158
  136. package/src/cli/commands/add.ts +0 -178
  137. package/src/cli/commands/build.ts +0 -73
  138. package/src/cli/commands/create.ts +0 -166
  139. package/src/cli/commands/dev.ts +0 -85
  140. package/src/cli/commands/generate.ts +0 -99
  141. package/src/cli/commands/help.ts +0 -95
  142. package/src/cli/commands/init.ts +0 -91
  143. package/src/cli/commands/version.ts +0 -38
  144. package/src/cli/index.ts +0 -6
  145. package/src/cli/templates/generators.ts +0 -359
  146. package/src/cli/templates/index.ts +0 -680
  147. package/src/cli/utils/exec.ts +0 -52
  148. package/src/cli/utils/file-system.ts +0 -78
  149. package/src/cli/utils/logger.ts +0 -111
  150. package/src/core/adapter.ts +0 -88
  151. package/src/core/application.ts +0 -1453
  152. package/src/core/context-pool.ts +0 -79
  153. package/src/core/context.ts +0 -856
  154. package/src/core/index.ts +0 -94
  155. package/src/core/middleware.ts +0 -272
  156. package/src/core/performance/buffer-pool.ts +0 -108
  157. package/src/core/performance/middleware-optimizer.ts +0 -162
  158. package/src/core/plugin/PluginManager.ts +0 -435
  159. package/src/core/plugin/builder.ts +0 -358
  160. package/src/core/plugin/index.ts +0 -50
  161. package/src/core/plugin/types.ts +0 -214
  162. package/src/core/router/file-router.ts +0 -623
  163. package/src/core/router/index.ts +0 -260
  164. package/src/core/router/radix-tree.ts +0 -242
  165. package/src/core/serializer.ts +0 -397
  166. package/src/core/store/index.ts +0 -30
  167. package/src/core/store/registry.ts +0 -178
  168. package/src/core/store/request-store.ts +0 -240
  169. package/src/core/store/types.ts +0 -233
  170. package/src/core/types.ts +0 -616
  171. package/src/database/adapter.ts +0 -35
  172. package/src/database/adapters/index.ts +0 -1
  173. package/src/database/adapters/mysql.ts +0 -669
  174. package/src/database/database.ts +0 -70
  175. package/src/database/dialect.ts +0 -388
  176. package/src/database/index.ts +0 -12
  177. package/src/database/migrations.ts +0 -86
  178. package/src/database/optimizer.ts +0 -125
  179. package/src/database/query-builder.ts +0 -404
  180. package/src/database/realtime.ts +0 -53
  181. package/src/database/schema.ts +0 -71
  182. package/src/database/transactions.ts +0 -56
  183. package/src/database/types.ts +0 -87
  184. package/src/deployment/cluster.ts +0 -471
  185. package/src/deployment/config.ts +0 -454
  186. package/src/deployment/docker.ts +0 -599
  187. package/src/deployment/graceful-shutdown.ts +0 -373
  188. package/src/deployment/index.ts +0 -56
  189. package/src/index.ts +0 -281
  190. package/src/security/adapter.ts +0 -318
  191. package/src/security/auth/JWTPlugin.ts +0 -234
  192. package/src/security/auth/JWTProvider.ts +0 -316
  193. package/src/security/auth/adapter.ts +0 -12
  194. package/src/security/auth/jwt.ts +0 -234
  195. package/src/security/auth/middleware.ts +0 -188
  196. package/src/security/csrf.ts +0 -220
  197. package/src/security/headers.ts +0 -108
  198. package/src/security/index.ts +0 -60
  199. package/src/security/rate-limit/adapter.ts +0 -7
  200. package/src/security/rate-limit/memory.ts +0 -108
  201. package/src/security/rate-limit/middleware.ts +0 -181
  202. package/src/security/sanitization.ts +0 -75
  203. package/src/security/types.ts +0 -240
  204. package/src/security/utils.ts +0 -52
  205. package/tsconfig.json +0 -39
@@ -1,582 +0,0 @@
1
- # Common Examples
2
-
3
- ## Table of Contents
4
-
5
- 1. [REST API](#rest-api)
6
- 2. [Authentication](#authentication)
7
- 3. [File Upload](#file-upload)
8
- 4. [Real-Time Chat](#real-time-chat)
9
- 5. [API Gateway](#api-gateway)
10
- 6. [Blog Platform](#blog-platform)
11
- 7. [E-Commerce](#e-commerce)
12
-
13
- ---
14
-
15
- ## REST API
16
-
17
- Complete RESTful API with CRUD operations:
18
-
19
- ```typescript
20
- import { createApp, z, logger, cors } from './nexus';
21
-
22
- const app = createApp();
23
-
24
- app.use(logger());
25
- app.use(cors());
26
-
27
- // In-memory database
28
- const users = new Map<string, User>();
29
-
30
- // List users
31
- app.get('/api/users', {
32
- schema: {
33
- query: z.object({
34
- page: z.string().transform(Number).default('1'),
35
- limit: z.string().transform(Number).default('10')
36
- })
37
- },
38
- handler: async (ctx) => {
39
- const { page, limit } = ctx.query;
40
- const allUsers = Array.from(users.values());
41
- const start = (page - 1) * limit;
42
- const paginatedUsers = allUsers.slice(start, start + limit);
43
-
44
- return {
45
- users: paginatedUsers,
46
- pagination: {
47
- page,
48
- limit,
49
- total: allUsers.length
50
- }
51
- };
52
- }
53
- });
54
-
55
- // Get user by ID
56
- app.get('/api/users/:id', async (ctx) => {
57
- const user = users.get(ctx.params.id);
58
-
59
- if (!user) {
60
- return ctx.response.status(404).json({
61
- error: 'User not found'
62
- });
63
- }
64
-
65
- return { user };
66
- });
67
-
68
- // Create user
69
- app.post('/api/users', {
70
- schema: {
71
- body: z.object({
72
- name: z.string().min(2),
73
- email: z.string().email(),
74
- age: z.number().min(18).optional()
75
- })
76
- },
77
- handler: async (ctx) => {
78
- const id = crypto.randomUUID();
79
- const user = { id, ...ctx.body, createdAt: new Date() };
80
- users.set(id, user);
81
-
82
- return ctx.response.status(201).json({ user });
83
- }
84
- });
85
-
86
- // Update user
87
- app.put('/api/users/:id', {
88
- schema: {
89
- body: z.object({
90
- name: z.string().min(2).optional(),
91
- email: z.string().email().optional(),
92
- age: z.number().min(18).optional()
93
- })
94
- },
95
- handler: async (ctx) => {
96
- const user = users.get(ctx.params.id);
97
-
98
- if (!user) {
99
- return ctx.response.status(404).json({
100
- error: 'User not found'
101
- });
102
- }
103
-
104
- const updated = { ...user, ...ctx.body };
105
- users.set(ctx.params.id, updated);
106
-
107
- return { user: updated };
108
- }
109
- });
110
-
111
- // Delete user
112
- app.delete('/api/users/:id', async (ctx) => {
113
- if (!users.has(ctx.params.id)) {
114
- return ctx.response.status(404).json({
115
- error: 'User not found'
116
- });
117
- }
118
-
119
- users.delete(ctx.params.id);
120
- return ctx.response.status(204).json({});
121
- });
122
-
123
- app.listen(3000);
124
- ```
125
-
126
- ---
127
-
128
- ## Authentication
129
-
130
- JWT-based authentication system:
131
-
132
- ```typescript
133
- import { createApp, z } from './nexus';
134
- import jwt from 'jsonwebtoken';
135
- import bcrypt from 'bcrypt';
136
-
137
- const app = createApp();
138
- const SECRET = process.env.JWT_SECRET || 'secret';
139
- const users = new Map<string, any>();
140
-
141
- // Register
142
- app.post('/auth/register', {
143
- schema: {
144
- body: z.object({
145
- email: z.string().email(),
146
- password: z.string().min(8),
147
- name: z.string()
148
- })
149
- },
150
- handler: async (ctx) => {
151
- const { email, password, name } = ctx.body;
152
-
153
- if (users.has(email)) {
154
- return ctx.response.status(400).json({
155
- error: 'User already exists'
156
- });
157
- }
158
-
159
- const hashedPassword = await bcrypt.hash(password, 10);
160
- const user = { email, password: hashedPassword, name };
161
- users.set(email, user);
162
-
163
- return { message: 'User registered successfully' };
164
- }
165
- });
166
-
167
- // Login
168
- app.post('/auth/login', {
169
- schema: {
170
- body: z.object({
171
- email: z.string().email(),
172
- password: z.string()
173
- })
174
- },
175
- handler: async (ctx) => {
176
- const { email, password } = ctx.body;
177
- const user = users.get(email);
178
-
179
- if (!user || !(await bcrypt.compare(password, user.password))) {
180
- return ctx.response.status(401).json({
181
- error: 'Invalid credentials'
182
- });
183
- }
184
-
185
- const token = jwt.sign({ email }, SECRET, { expiresIn: '1h' });
186
-
187
- return { token, user: { email: user.email, name: user.name } };
188
- }
189
- });
190
-
191
- // Auth middleware
192
- const authenticate = async (ctx: any, next: any) => {
193
- const token = ctx.headers.authorization?.replace('Bearer ', '');
194
-
195
- if (!token) {
196
- return ctx.response.status(401).json({
197
- error: 'Unauthorized'
198
- });
199
- }
200
-
201
- try {
202
- const decoded = jwt.verify(token, SECRET) as any;
203
- ctx.user = users.get(decoded.email);
204
- return next(ctx);
205
- } catch (error) {
206
- return ctx.response.status(401).json({
207
- error: 'Invalid token'
208
- });
209
- }
210
- };
211
-
212
- // Protected route
213
- app.get('/profile', {
214
- middlewares: [authenticate],
215
- handler: async (ctx: any) => {
216
- return { user: ctx.user };
217
- }
218
- });
219
-
220
- app.listen(3000);
221
- ```
222
-
223
- ---
224
-
225
- ## File Upload
226
-
227
- Handle file uploads with validation:
228
-
229
- ```typescript
230
- import { createApp } from './nexus';
231
- import multer from 'multer';
232
- import path from 'path';
233
-
234
- const app = createApp();
235
-
236
- const storage = multer.diskStorage({
237
- destination: './uploads/',
238
- filename: (req, file, cb) => {
239
- const uniqueName = `${Date.now()}-${file.originalname}`;
240
- cb(null, uniqueName);
241
- }
242
- });
243
-
244
- const upload = multer({
245
- storage,
246
- limits: { fileSize: 5 * 1024 * 1024 }, // 5MB
247
- fileFilter: (req, file, cb) => {
248
- const allowedTypes = ['image/jpeg', 'image/png', 'image/webp'];
249
- if (allowedTypes.includes(file.mimetype)) {
250
- cb(null, true);
251
- } else {
252
- cb(new Error('Invalid file type'));
253
- }
254
- }
255
- });
256
-
257
- // Upload endpoint
258
- app.post('/upload', async (ctx) => {
259
- // Use multer middleware (needs integration)
260
- // In a real app, you'd integrate multer properly
261
-
262
- return {
263
- message: 'File uploaded successfully',
264
- file: {
265
- filename: 'example.jpg',
266
- size: 12345,
267
- url: '/uploads/example.jpg'
268
- }
269
- };
270
- });
271
-
272
- // Download endpoint
273
- app.get('/uploads/:filename', async (ctx) => {
274
- const { filename } = ctx.params;
275
- const filepath = path.join(__dirname, 'uploads', filename);
276
-
277
- // Security: validate filename
278
- if (filename.includes('..')) {
279
- return ctx.response.status(400).json({
280
- error: 'Invalid filename'
281
- });
282
- }
283
-
284
- const stream = createReadStream(filepath);
285
- return ctx.stream(stream);
286
- });
287
-
288
- app.listen(3000);
289
- ```
290
-
291
- ---
292
-
293
- ## Real-Time Chat
294
-
295
- Simple chat application (conceptual):
296
-
297
- ```typescript
298
- import { createApp } from './nexus';
299
-
300
- const app = createApp();
301
-
302
- // Store messages in memory
303
- const messages: Array<{ id: string; user: string; text: string; timestamp: Date }> = [];
304
- const clients: Set<any> = new Set();
305
-
306
- // Get messages
307
- app.get('/api/messages', async (ctx) => {
308
- return { messages };
309
- });
310
-
311
- // Post message
312
- app.post('/api/messages', {
313
- schema: {
314
- body: z.object({
315
- user: z.string(),
316
- text: z.string().min(1).max(500)
317
- })
318
- },
319
- handler: async (ctx) => {
320
- const message = {
321
- id: crypto.randomUUID(),
322
- ...ctx.body,
323
- timestamp: new Date()
324
- };
325
-
326
- messages.push(message);
327
-
328
- // Broadcast to all clients (WebSocket would be here)
329
- // clients.forEach(client => client.send(message));
330
-
331
- return { message };
332
- }
333
- });
334
-
335
- // Delete message
336
- app.delete('/api/messages/:id', async (ctx) => {
337
- const index = messages.findIndex(m => m.id === ctx.params.id);
338
-
339
- if (index === -1) {
340
- return ctx.response.status(404).json({
341
- error: 'Message not found'
342
- });
343
- }
344
-
345
- messages.splice(index, 1);
346
- return ctx.response.status(204).json({});
347
- });
348
-
349
- app.listen(3000);
350
- ```
351
-
352
- ---
353
-
354
- ## API Gateway
355
-
356
- Proxy requests to multiple services:
357
-
358
- ```typescript
359
- import { createApp } from './nexus';
360
- import fetch from 'node-fetch';
361
-
362
- const app = createApp();
363
-
364
- const services = {
365
- users: 'http://users-service:3001',
366
- posts: 'http://posts-service:3002',
367
- comments: 'http://comments-service:3003'
368
- };
369
-
370
- // Proxy middleware
371
- const proxy = (service: string) => {
372
- return async (ctx: any) => {
373
- const targetUrl = `${services[service as keyof typeof services]}${ctx.path}`;
374
-
375
- const response = await fetch(targetUrl, {
376
- method: ctx.method,
377
- headers: ctx.headers as any,
378
- body: ctx.method !== 'GET' ? JSON.stringify(ctx.body) : undefined
379
- });
380
-
381
- const data = await response.json();
382
- return ctx.response.status(response.status).json(data);
383
- };
384
- };
385
-
386
- // Route to services
387
- app.get('/api/users/*', proxy('users'));
388
- app.post('/api/users/*', proxy('users'));
389
-
390
- app.get('/api/posts/*', proxy('posts'));
391
- app.post('/api/posts/*', proxy('posts'));
392
-
393
- app.get('/api/comments/*', proxy('comments'));
394
- app.post('/api/comments/*', proxy('comments'));
395
-
396
- // Health check
397
- app.get('/health', async (ctx) => {
398
- return { status: 'ok', timestamp: new Date() };
399
- });
400
-
401
- app.listen(3000);
402
- ```
403
-
404
- ---
405
-
406
- ## Blog Platform
407
-
408
- Complete blog with posts and comments:
409
-
410
- ```typescript
411
- import { createApp, z } from './nexus';
412
-
413
- const app = createApp();
414
-
415
- const posts = new Map<string, any>();
416
- const comments = new Map<string, any[]>();
417
-
418
- // List posts
419
- app.get('/api/posts', async (ctx) => {
420
- return { posts: Array.from(posts.values()) };
421
- });
422
-
423
- // Get post
424
- app.get('/api/posts/:id', async (ctx) => {
425
- const post = posts.get(ctx.params.id);
426
- if (!post) {
427
- return ctx.response.status(404).json({ error: 'Post not found' });
428
- }
429
-
430
- return {
431
- post,
432
- comments: comments.get(ctx.params.id) || []
433
- };
434
- });
435
-
436
- // Create post
437
- app.post('/api/posts', {
438
- schema: {
439
- body: z.object({
440
- title: z.string().min(3).max(200),
441
- content: z.string().min(10),
442
- author: z.string()
443
- })
444
- },
445
- handler: async (ctx) => {
446
- const id = crypto.randomUUID();
447
- const post = {
448
- id,
449
- ...ctx.body,
450
- createdAt: new Date(),
451
- views: 0
452
- };
453
-
454
- posts.set(id, post);
455
- comments.set(id, []);
456
-
457
- return ctx.response.status(201).json({ post });
458
- }
459
- });
460
-
461
- // Add comment
462
- app.post('/api/posts/:id/comments', {
463
- schema: {
464
- body: z.object({
465
- author: z.string(),
466
- text: z.string().min(1).max(1000)
467
- })
468
- },
469
- handler: async (ctx) => {
470
- if (!posts.has(ctx.params.id)) {
471
- return ctx.response.status(404).json({ error: 'Post not found' });
472
- }
473
-
474
- const comment = {
475
- id: crypto.randomUUID(),
476
- postId: ctx.params.id,
477
- ...ctx.body,
478
- createdAt: new Date()
479
- };
480
-
481
- const postComments = comments.get(ctx.params.id) || [];
482
- postComments.push(comment);
483
- comments.set(ctx.params.id, postComments);
484
-
485
- return { comment };
486
- }
487
- });
488
-
489
- app.listen(3000);
490
- ```
491
-
492
- ---
493
-
494
- ## E-Commerce
495
-
496
- Product catalog with cart:
497
-
498
- ```typescript
499
- import { createApp, z } from './nexus';
500
-
501
- const app = createApp();
502
-
503
- const products = new Map<string, any>();
504
- const carts = new Map<string, any[]>();
505
-
506
- // Seed products
507
- products.set('1', { id: '1', name: 'Laptop', price: 999, stock: 10 });
508
- products.set('2', { id: '2', name: 'Mouse', price: 29, stock: 50 });
509
- products.set('3', { id: '3', name: 'Keyboard', price: 79, stock: 30 });
510
-
511
- // List products
512
- app.get('/api/products', async (ctx) => {
513
- return { products: Array.from(products.values()) };
514
- });
515
-
516
- // Get cart
517
- app.get('/api/cart/:userId', async (ctx) => {
518
- const cart = carts.get(ctx.params.userId) || [];
519
- const total = cart.reduce((sum, item) => sum + item.price * item.quantity, 0);
520
-
521
- return { cart, total };
522
- });
523
-
524
- // Add to cart
525
- app.post('/api/cart/:userId', {
526
- schema: {
527
- body: z.object({
528
- productId: z.string(),
529
- quantity: z.number().min(1)
530
- })
531
- },
532
- handler: async (ctx) => {
533
- const product = products.get(ctx.body.productId);
534
-
535
- if (!product) {
536
- return ctx.response.status(404).json({ error: 'Product not found' });
537
- }
538
-
539
- if (product.stock < ctx.body.quantity) {
540
- return ctx.response.status(400).json({ error: 'Insufficient stock' });
541
- }
542
-
543
- const cart = carts.get(ctx.params.userId) || [];
544
- cart.push({
545
- ...product,
546
- quantity: ctx.body.quantity
547
- });
548
- carts.set(ctx.params.userId, cart);
549
-
550
- return { cart };
551
- }
552
- });
553
-
554
- // Checkout
555
- app.post('/api/checkout/:userId', async (ctx) => {
556
- const cart = carts.get(ctx.params.userId) || [];
557
-
558
- if (cart.length === 0) {
559
- return ctx.response.status(400).json({ error: 'Cart is empty' });
560
- }
561
-
562
- const total = cart.reduce((sum, item) => sum + item.price * item.quantity, 0);
563
-
564
- // Clear cart
565
- carts.delete(ctx.params.userId);
566
-
567
- return {
568
- order: {
569
- id: crypto.randomUUID(),
570
- items: cart,
571
- total,
572
- timestamp: new Date()
573
- }
574
- };
575
- });
576
-
577
- app.listen(3000);
578
- ```
579
-
580
- ---
581
-
582
- [← API Reference](./09-api-reference.md)