@engjts/nexus 0.1.8 → 0.1.10

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 (221) hide show
  1. package/dist/advanced/playground/playground.js.map +1 -1
  2. package/dist/advanced/static/generateDirectoryListing.d.ts +1 -1
  3. package/dist/advanced/static/generateDirectoryListing.d.ts.map +1 -1
  4. package/dist/advanced/static/generateDirectoryListing.js +12 -6
  5. package/dist/advanced/static/generateDirectoryListing.js.map +1 -1
  6. package/dist/advanced/static/index.d.ts +2 -0
  7. package/dist/advanced/static/index.d.ts.map +1 -1
  8. package/dist/advanced/static/index.js +4 -1
  9. package/dist/advanced/static/index.js.map +1 -1
  10. package/dist/advanced/static/serveStatic.d.ts.map +1 -1
  11. package/dist/advanced/static/serveStatic.js +7 -1
  12. package/dist/advanced/static/serveStatic.js.map +1 -1
  13. package/dist/index.d.ts +1 -1
  14. package/dist/index.d.ts.map +1 -1
  15. package/dist/index.js +3 -1
  16. package/dist/index.js.map +1 -1
  17. package/package.json +1 -1
  18. package/BENCHMARK_REPORT.md +0 -343
  19. package/documentation/01-getting-started.md +0 -240
  20. package/documentation/02-context.md +0 -335
  21. package/documentation/03-routing.md +0 -397
  22. package/documentation/04-middleware.md +0 -483
  23. package/documentation/05-validation.md +0 -514
  24. package/documentation/06-error-handling.md +0 -465
  25. package/documentation/07-performance.md +0 -364
  26. package/documentation/08-adapters.md +0 -470
  27. package/documentation/09-api-reference.md +0 -548
  28. package/documentation/10-examples.md +0 -582
  29. package/documentation/11-deployment.md +0 -477
  30. package/documentation/12-sentry.md +0 -620
  31. package/documentation/13-sentry-data-storage.md +0 -996
  32. package/documentation/14-sentry-data-reference.md +0 -457
  33. package/documentation/15-sentry-summary.md +0 -409
  34. package/documentation/16-alerts-system.md +0 -745
  35. package/documentation/17-alert-adapters.md +0 -696
  36. package/documentation/18-alerts-implementation-summary.md +0 -385
  37. package/documentation/19-class-based-routing.md +0 -840
  38. package/documentation/20-websocket-realtime.md +0 -813
  39. package/documentation/21-cache-system.md +0 -510
  40. package/documentation/22-job-queue.md +0 -772
  41. package/documentation/23-sentry-plugin.md +0 -551
  42. package/documentation/24-testing-utilities.md +0 -1287
  43. package/documentation/25-api-versioning.md +0 -533
  44. package/documentation/26-context-store.md +0 -607
  45. package/documentation/27-dependency-injection.md +0 -329
  46. package/documentation/28-lifecycle-hooks.md +0 -521
  47. package/documentation/29-package-structure.md +0 -196
  48. package/documentation/30-plugin-system.md +0 -414
  49. package/documentation/31-jwt-authentication.md +0 -597
  50. package/documentation/32-cli.md +0 -268
  51. package/documentation/ALERTS-COMPLETE-SUMMARY.md +0 -429
  52. package/documentation/ALERTS-INDEX.md +0 -330
  53. package/documentation/ALERTS-QUICK-REFERENCE.md +0 -286
  54. package/documentation/README.md +0 -178
  55. package/documentation/index.html +0 -34
  56. package/modern_framework_paper.md +0 -1870
  57. package/public/css/style.css +0 -87
  58. package/public/index.html +0 -34
  59. package/public/js/app.js +0 -27
  60. package/src/advanced/cache/InMemoryCacheStore.ts +0 -68
  61. package/src/advanced/cache/MultiTierCache.ts +0 -194
  62. package/src/advanced/cache/RedisCacheStore.ts +0 -341
  63. package/src/advanced/cache/index.ts +0 -5
  64. package/src/advanced/cache/types.ts +0 -40
  65. package/src/advanced/graphql/SimpleDataLoader.ts +0 -42
  66. package/src/advanced/graphql/index.ts +0 -22
  67. package/src/advanced/graphql/server.ts +0 -252
  68. package/src/advanced/graphql/types.ts +0 -42
  69. package/src/advanced/jobs/InMemoryQueueStore.ts +0 -68
  70. package/src/advanced/jobs/JobQueue.ts +0 -556
  71. package/src/advanced/jobs/RedisQueueStore.ts +0 -367
  72. package/src/advanced/jobs/index.ts +0 -5
  73. package/src/advanced/jobs/types.ts +0 -70
  74. package/src/advanced/observability/APMManager.ts +0 -163
  75. package/src/advanced/observability/AlertManager.ts +0 -109
  76. package/src/advanced/observability/MetricRegistry.ts +0 -151
  77. package/src/advanced/observability/ObservabilityCenter.ts +0 -304
  78. package/src/advanced/observability/StructuredLogger.ts +0 -154
  79. package/src/advanced/observability/TracingManager.ts +0 -117
  80. package/src/advanced/observability/adapters.ts +0 -304
  81. package/src/advanced/observability/createObservabilityMiddleware.ts +0 -63
  82. package/src/advanced/observability/index.ts +0 -11
  83. package/src/advanced/observability/types.ts +0 -174
  84. package/src/advanced/playground/extractPathParams.ts +0 -6
  85. package/src/advanced/playground/generateFieldExample.ts +0 -31
  86. package/src/advanced/playground/generatePlaygroundHTML.ts +0 -1956
  87. package/src/advanced/playground/generateSummary.ts +0 -19
  88. package/src/advanced/playground/getTagFromPath.ts +0 -9
  89. package/src/advanced/playground/index.ts +0 -8
  90. package/src/advanced/playground/playground.ts +0 -250
  91. package/src/advanced/playground/types.ts +0 -49
  92. package/src/advanced/playground/zodToExample.ts +0 -16
  93. package/src/advanced/playground/zodToParams.ts +0 -15
  94. package/src/advanced/postman/buildAuth.ts +0 -31
  95. package/src/advanced/postman/buildBody.ts +0 -15
  96. package/src/advanced/postman/buildQueryParams.ts +0 -27
  97. package/src/advanced/postman/buildRequestItem.ts +0 -36
  98. package/src/advanced/postman/buildResponses.ts +0 -11
  99. package/src/advanced/postman/buildUrl.ts +0 -33
  100. package/src/advanced/postman/capitalize.ts +0 -4
  101. package/src/advanced/postman/generateCollection.ts +0 -59
  102. package/src/advanced/postman/generateEnvironment.ts +0 -34
  103. package/src/advanced/postman/generateExampleFromZod.ts +0 -21
  104. package/src/advanced/postman/generateFieldExample.ts +0 -45
  105. package/src/advanced/postman/generateName.ts +0 -20
  106. package/src/advanced/postman/generateUUID.ts +0 -11
  107. package/src/advanced/postman/getTagFromPath.ts +0 -10
  108. package/src/advanced/postman/index.ts +0 -28
  109. package/src/advanced/postman/postman.ts +0 -156
  110. package/src/advanced/postman/slugify.ts +0 -7
  111. package/src/advanced/postman/types.ts +0 -140
  112. package/src/advanced/realtime/index.ts +0 -18
  113. package/src/advanced/realtime/websocket.ts +0 -231
  114. package/src/advanced/sentry/index.ts +0 -1236
  115. package/src/advanced/sentry/types.ts +0 -355
  116. package/src/advanced/static/generateDirectoryListing.ts +0 -47
  117. package/src/advanced/static/generateETag.ts +0 -7
  118. package/src/advanced/static/getMimeType.ts +0 -9
  119. package/src/advanced/static/index.ts +0 -32
  120. package/src/advanced/static/isSafePath.ts +0 -13
  121. package/src/advanced/static/publicDir.ts +0 -21
  122. package/src/advanced/static/serveStatic.ts +0 -225
  123. package/src/advanced/static/spa.ts +0 -24
  124. package/src/advanced/static/types.ts +0 -159
  125. package/src/advanced/swagger/SwaggerGenerator.ts +0 -66
  126. package/src/advanced/swagger/buildOperation.ts +0 -61
  127. package/src/advanced/swagger/buildParameters.ts +0 -61
  128. package/src/advanced/swagger/buildRequestBody.ts +0 -21
  129. package/src/advanced/swagger/buildResponses.ts +0 -54
  130. package/src/advanced/swagger/capitalize.ts +0 -5
  131. package/src/advanced/swagger/convertPath.ts +0 -9
  132. package/src/advanced/swagger/createSwagger.ts +0 -12
  133. package/src/advanced/swagger/generateOperationId.ts +0 -21
  134. package/src/advanced/swagger/generateSpec.ts +0 -105
  135. package/src/advanced/swagger/generateSummary.ts +0 -24
  136. package/src/advanced/swagger/generateSwaggerUI.ts +0 -70
  137. package/src/advanced/swagger/generateThemeCss.ts +0 -53
  138. package/src/advanced/swagger/index.ts +0 -25
  139. package/src/advanced/swagger/swagger.ts +0 -237
  140. package/src/advanced/swagger/types.ts +0 -206
  141. package/src/advanced/swagger/zodFieldToOpenAPI.ts +0 -94
  142. package/src/advanced/swagger/zodSchemaToOpenAPI.ts +0 -50
  143. package/src/advanced/swagger/zodToOpenAPI.ts +0 -22
  144. package/src/advanced/testing/factory.ts +0 -509
  145. package/src/advanced/testing/harness.ts +0 -612
  146. package/src/advanced/testing/index.ts +0 -430
  147. package/src/advanced/testing/load-test.ts +0 -618
  148. package/src/advanced/testing/mock-server.ts +0 -498
  149. package/src/advanced/testing/mock.ts +0 -670
  150. package/src/cli/bin.ts +0 -9
  151. package/src/cli/cli.ts +0 -158
  152. package/src/cli/commands/add.ts +0 -178
  153. package/src/cli/commands/build.ts +0 -73
  154. package/src/cli/commands/create.ts +0 -166
  155. package/src/cli/commands/dev.ts +0 -85
  156. package/src/cli/commands/generate.ts +0 -99
  157. package/src/cli/commands/help.ts +0 -95
  158. package/src/cli/commands/init.ts +0 -91
  159. package/src/cli/commands/version.ts +0 -38
  160. package/src/cli/index.ts +0 -6
  161. package/src/cli/templates/generators.ts +0 -359
  162. package/src/cli/templates/index.ts +0 -680
  163. package/src/cli/utils/exec.ts +0 -52
  164. package/src/cli/utils/file-system.ts +0 -78
  165. package/src/cli/utils/logger.ts +0 -111
  166. package/src/core/adapter.ts +0 -88
  167. package/src/core/application.ts +0 -1453
  168. package/src/core/context-pool.ts +0 -79
  169. package/src/core/context.ts +0 -856
  170. package/src/core/index.ts +0 -94
  171. package/src/core/middleware.ts +0 -272
  172. package/src/core/performance/buffer-pool.ts +0 -108
  173. package/src/core/performance/middleware-optimizer.ts +0 -162
  174. package/src/core/plugin/PluginManager.ts +0 -435
  175. package/src/core/plugin/builder.ts +0 -358
  176. package/src/core/plugin/index.ts +0 -50
  177. package/src/core/plugin/types.ts +0 -214
  178. package/src/core/router/file-router.ts +0 -623
  179. package/src/core/router/index.ts +0 -260
  180. package/src/core/router/radix-tree.ts +0 -242
  181. package/src/core/serializer.ts +0 -397
  182. package/src/core/store/index.ts +0 -30
  183. package/src/core/store/registry.ts +0 -178
  184. package/src/core/store/request-store.ts +0 -240
  185. package/src/core/store/types.ts +0 -233
  186. package/src/core/types.ts +0 -616
  187. package/src/database/adapter.ts +0 -35
  188. package/src/database/adapters/index.ts +0 -1
  189. package/src/database/adapters/mysql.ts +0 -669
  190. package/src/database/database.ts +0 -70
  191. package/src/database/dialect.ts +0 -388
  192. package/src/database/index.ts +0 -12
  193. package/src/database/migrations.ts +0 -86
  194. package/src/database/optimizer.ts +0 -125
  195. package/src/database/query-builder.ts +0 -404
  196. package/src/database/realtime.ts +0 -53
  197. package/src/database/schema.ts +0 -71
  198. package/src/database/transactions.ts +0 -56
  199. package/src/database/types.ts +0 -87
  200. package/src/deployment/cluster.ts +0 -471
  201. package/src/deployment/config.ts +0 -454
  202. package/src/deployment/docker.ts +0 -599
  203. package/src/deployment/graceful-shutdown.ts +0 -373
  204. package/src/deployment/index.ts +0 -56
  205. package/src/index.ts +0 -281
  206. package/src/security/adapter.ts +0 -318
  207. package/src/security/auth/JWTPlugin.ts +0 -234
  208. package/src/security/auth/JWTProvider.ts +0 -316
  209. package/src/security/auth/adapter.ts +0 -12
  210. package/src/security/auth/jwt.ts +0 -234
  211. package/src/security/auth/middleware.ts +0 -188
  212. package/src/security/csrf.ts +0 -220
  213. package/src/security/headers.ts +0 -108
  214. package/src/security/index.ts +0 -60
  215. package/src/security/rate-limit/adapter.ts +0 -7
  216. package/src/security/rate-limit/memory.ts +0 -108
  217. package/src/security/rate-limit/middleware.ts +0 -181
  218. package/src/security/sanitization.ts +0 -75
  219. package/src/security/types.ts +0 -240
  220. package/src/security/utils.ts +0 -52
  221. package/tsconfig.json +0 -39
@@ -1,813 +0,0 @@
1
- # WebSocket Realtime
2
-
3
- Nexus Framework menyediakan **WebSocket support** built-in untuk membangun aplikasi realtime seperti chat, notifications, live updates, dan lainnya.
4
-
5
- ## Quick Start
6
-
7
- ```typescript
8
- import { createApp } from 'nexus';
9
-
10
- const app = createApp();
11
-
12
- // Define WebSocket route
13
- app.ws('/ws/chat', {
14
- onConnect: async (socket, ctx) => {
15
- console.log('Client connected');
16
- socket.send(JSON.stringify({ type: 'welcome', message: 'Hello!' }));
17
- },
18
-
19
- onMessage: async (socket, message, ctx) => {
20
- console.log('Received:', message);
21
- socket.send(JSON.stringify({ echo: message }));
22
- },
23
-
24
- onClose: async (socket, ctx) => {
25
- console.log('Client disconnected');
26
- }
27
- });
28
-
29
- app.listen(3000, () => {
30
- console.log('Server running at http://localhost:3000');
31
- console.log('WebSocket at ws://localhost:3000/ws/chat');
32
- });
33
- ```
34
-
35
- ## WebSocketRouteConfig Interface
36
-
37
- ```typescript
38
- interface WebSocketRouteConfig {
39
- /** Authentication handler - validate token and return user object */
40
- auth?: (ctx: WebSocketContext) => Promise<any>;
41
-
42
- /** Called before onConnect - can be used for additional setup */
43
- beforeConnect?: (socket: WebSocket, ctx: WebSocketContext) => Promise<void>;
44
-
45
- /** Called when client connects */
46
- onConnect?: (socket: WebSocket, ctx: WebSocketContext) => Promise<void>;
47
-
48
- /** Called when client sends a message */
49
- onMessage?: (socket: WebSocket, message: any, ctx: WebSocketContext) => Promise<void>;
50
-
51
- /** Called when client disconnects */
52
- onClose?: (socket: WebSocket, ctx: WebSocketContext, code: number, reason?: Buffer) => Promise<void>;
53
-
54
- /** Called on WebSocket error */
55
- onError?: (socket: WebSocket, error: Error, ctx: WebSocketContext) => Promise<void>;
56
-
57
- /** Enable room support (default: true) */
58
- rooms?: boolean;
59
- }
60
- ```
61
-
62
- ## WebSocketContext
63
-
64
- Context object yang tersedia di setiap handler:
65
-
66
- ```typescript
67
- interface WebSocketContext {
68
- /** WebSocket path (e.g., '/ws/chat') */
69
- path: string;
70
-
71
- /** Query parameters from URL */
72
- query: Record<string, string | string[]>;
73
-
74
- /** Request headers */
75
- headers: IncomingMessage['headers'];
76
-
77
- /** User object (set by auth handler) */
78
- user?: any;
79
-
80
- /** Custom metadata */
81
- metadata?: Record<string, any>;
82
-
83
- /** Raw Node.js request */
84
- raw: {
85
- req: IncomingMessage;
86
- };
87
- }
88
- ```
89
-
90
- ## Fitur-Fitur
91
-
92
- ### 1. Authentication
93
-
94
- Validasi token/credentials sebelum koneksi diterima:
95
-
96
- ```typescript
97
- app.ws('/ws/protected', {
98
- auth: async (ctx) => {
99
- const token = ctx.query.token as string;
100
-
101
- if (!token) {
102
- throw new Error('Token required');
103
- }
104
-
105
- // Verify JWT token
106
- const user = await verifyToken(token);
107
-
108
- if (!user) {
109
- throw new Error('Invalid token');
110
- }
111
-
112
- // Return user object - will be available as ctx.user
113
- return user;
114
- },
115
-
116
- onConnect: async (socket, ctx) => {
117
- // ctx.user is now available
118
- console.log(`User ${ctx.user.name} connected`);
119
-
120
- socket.send(JSON.stringify({
121
- type: 'authenticated',
122
- user: ctx.user
123
- }));
124
- },
125
-
126
- onMessage: async (socket, message, ctx) => {
127
- console.log(`Message from ${ctx.user.name}:`, message);
128
- }
129
- });
130
- ```
131
-
132
- **Client connection:**
133
- ```javascript
134
- const ws = new WebSocket('ws://localhost:3000/ws/protected?token=your-jwt-token');
135
- ```
136
-
137
- ### 2. Room Management
138
-
139
- Kelompokkan koneksi ke dalam "rooms" untuk broadcast targeted:
140
-
141
- ```typescript
142
- app.ws('/ws/chat', {
143
- onConnect: async (socket, ctx) => {
144
- const ws = app.getWebSocket()!;
145
- const room = ctx.query.room as string || 'general';
146
-
147
- // Join a room
148
- ws.joinRoom(room, socket);
149
-
150
- // Notify others in the room
151
- ws.broadcast(room, {
152
- type: 'user_joined',
153
- message: `New user joined ${room}`
154
- });
155
-
156
- socket.send(JSON.stringify({
157
- type: 'joined',
158
- room
159
- }));
160
- },
161
-
162
- onMessage: async (socket, message, ctx) => {
163
- const ws = app.getWebSocket()!;
164
-
165
- if (message.type === 'chat') {
166
- // Broadcast to current room
167
- ws.broadcast(message.room, {
168
- type: 'chat',
169
- user: ctx.user?.name,
170
- text: message.text,
171
- timestamp: new Date().toISOString()
172
- });
173
- }
174
-
175
- if (message.type === 'join_room') {
176
- ws.joinRoom(message.room, socket);
177
- socket.send(JSON.stringify({ type: 'room_joined', room: message.room }));
178
- }
179
-
180
- if (message.type === 'leave_room') {
181
- ws.leaveRoom(message.room, socket);
182
- socket.send(JSON.stringify({ type: 'room_left', room: message.room }));
183
- }
184
- },
185
-
186
- onClose: async (socket, ctx) => {
187
- const ws = app.getWebSocket()!;
188
-
189
- // Broadcast user left (rooms are auto-cleaned)
190
- ws.broadcast('general', {
191
- type: 'user_left',
192
- user: ctx.user?.name
193
- });
194
- }
195
- });
196
- ```
197
-
198
- ### 3. Broadcasting
199
-
200
- Kirim pesan ke semua client dalam room:
201
-
202
- ```typescript
203
- const ws = app.getWebSocket()!;
204
-
205
- // Broadcast to specific room
206
- ws.broadcast('notifications', {
207
- type: 'alert',
208
- message: 'System maintenance in 5 minutes'
209
- });
210
-
211
- // Broadcast to multiple rooms
212
- ['room1', 'room2', 'room3'].forEach(room => {
213
- ws.broadcast(room, { type: 'announcement', text: 'Hello everyone!' });
214
- });
215
- ```
216
-
217
- ### 4. Room Manager API
218
-
219
- ```typescript
220
- const ws = app.getWebSocket()!;
221
-
222
- // Create room explicitly
223
- ws.createRoom('vip-lounge');
224
-
225
- // Join room
226
- ws.joinRoom('vip-lounge', socket);
227
-
228
- // Leave room
229
- ws.leaveRoom('vip-lounge', socket);
230
-
231
- // Broadcast to room
232
- ws.broadcast('vip-lounge', { message: 'VIP announcement' });
233
-
234
- // Using roomManager helper
235
- ws.roomManager.create('new-room');
236
- ws.roomManager.join('new-room', socket);
237
- ws.roomManager.leave('new-room', socket);
238
- ws.roomManager.broadcast('new-room', { data: 'hello' });
239
- ws.roomManager.list(); // ['new-room', 'vip-lounge', ...]
240
- ```
241
-
242
- ## Contoh Penggunaan
243
-
244
- ### Chat Application
245
-
246
- ```typescript
247
- app.ws('/ws/chat', {
248
- auth: async (ctx) => {
249
- const token = ctx.query.token as string;
250
- return { id: generateId(), name: token || 'Anonymous' };
251
- },
252
-
253
- onConnect: async (socket, ctx) => {
254
- const ws = app.getWebSocket()!;
255
-
256
- // Join default room
257
- ws.joinRoom('general', socket);
258
-
259
- // Welcome message
260
- socket.send(JSON.stringify({
261
- type: 'welcome',
262
- message: `Welcome ${ctx.user.name}!`,
263
- room: 'general'
264
- }));
265
-
266
- // Notify others
267
- ws.broadcast('general', {
268
- type: 'system',
269
- message: `${ctx.user.name} joined the chat`
270
- });
271
- },
272
-
273
- onMessage: async (socket, message, ctx) => {
274
- const ws = app.getWebSocket()!;
275
-
276
- switch (message.type) {
277
- case 'chat':
278
- ws.broadcast(message.room || 'general', {
279
- type: 'chat',
280
- user: ctx.user.name,
281
- text: message.text,
282
- timestamp: new Date().toISOString()
283
- });
284
- break;
285
-
286
- case 'private':
287
- // Private message implementation
288
- // Find target socket and send directly
289
- break;
290
-
291
- case 'typing':
292
- ws.broadcast(message.room || 'general', {
293
- type: 'typing',
294
- user: ctx.user.name
295
- });
296
- break;
297
- }
298
- },
299
-
300
- onClose: async (socket, ctx) => {
301
- const ws = app.getWebSocket()!;
302
- ws.broadcast('general', {
303
- type: 'system',
304
- message: `${ctx.user.name} left the chat`
305
- });
306
- },
307
-
308
- onError: async (socket, error, ctx) => {
309
- console.error(`WebSocket error for ${ctx.user?.name}:`, error);
310
- }
311
- });
312
- ```
313
-
314
- ### Notifications Service
315
-
316
- ```typescript
317
- app.ws('/ws/notifications', {
318
- auth: async (ctx) => {
319
- const token = ctx.query.token as string;
320
- return await verifyToken(token);
321
- },
322
-
323
- onConnect: async (socket, ctx) => {
324
- const ws = app.getWebSocket()!;
325
-
326
- // Join user-specific room
327
- ws.joinRoom(`user:${ctx.user.id}`, socket);
328
-
329
- // Join role-based rooms
330
- ctx.user.roles.forEach((role: string) => {
331
- ws.joinRoom(`role:${role}`, socket);
332
- });
333
-
334
- // Send unread notifications
335
- const unread = await getUnreadNotifications(ctx.user.id);
336
- socket.send(JSON.stringify({
337
- type: 'unread',
338
- count: unread.length,
339
- notifications: unread
340
- }));
341
- },
342
-
343
- onMessage: async (socket, message, ctx) => {
344
- if (message.type === 'mark_read') {
345
- await markNotificationRead(message.notificationId, ctx.user.id);
346
- socket.send(JSON.stringify({
347
- type: 'marked_read',
348
- notificationId: message.notificationId
349
- }));
350
- }
351
- }
352
- });
353
-
354
- // Send notification from anywhere in your app
355
- function sendNotification(userId: string, notification: any) {
356
- const ws = app.getWebSocket();
357
- ws?.broadcast(`user:${userId}`, {
358
- type: 'notification',
359
- ...notification
360
- });
361
- }
362
-
363
- // Send to all admins
364
- function notifyAdmins(message: string) {
365
- const ws = app.getWebSocket();
366
- ws?.broadcast('role:admin', {
367
- type: 'admin_alert',
368
- message
369
- });
370
- }
371
- ```
372
-
373
- ### Live Updates / Real-time Data
374
-
375
- ```typescript
376
- app.ws('/ws/stocks', {
377
- onConnect: async (socket, ctx) => {
378
- const ws = app.getWebSocket()!;
379
- const symbols = (ctx.query.symbols as string)?.split(',') || ['AAPL', 'GOOGL'];
380
-
381
- // Subscribe to stock symbols
382
- symbols.forEach(symbol => {
383
- ws.joinRoom(`stock:${symbol}`, socket);
384
- });
385
-
386
- socket.send(JSON.stringify({
387
- type: 'subscribed',
388
- symbols
389
- }));
390
- },
391
-
392
- onMessage: async (socket, message, ctx) => {
393
- const ws = app.getWebSocket()!;
394
-
395
- if (message.type === 'subscribe') {
396
- ws.joinRoom(`stock:${message.symbol}`, socket);
397
- }
398
-
399
- if (message.type === 'unsubscribe') {
400
- ws.leaveRoom(`stock:${message.symbol}`, socket);
401
- }
402
- }
403
- });
404
-
405
- // Price update service (simulated)
406
- setInterval(() => {
407
- const ws = app.getWebSocket();
408
- const stocks = ['AAPL', 'GOOGL', 'MSFT', 'AMZN'];
409
-
410
- stocks.forEach(symbol => {
411
- ws?.broadcast(`stock:${symbol}`, {
412
- type: 'price_update',
413
- symbol,
414
- price: (Math.random() * 1000).toFixed(2),
415
- timestamp: new Date().toISOString()
416
- });
417
- });
418
- }, 1000);
419
- ```
420
-
421
- ### Multiplayer Game
422
-
423
- ```typescript
424
- app.ws('/ws/game', {
425
- auth: async (ctx) => {
426
- return {
427
- id: generatePlayerId(),
428
- name: ctx.query.name as string || 'Player'
429
- };
430
- },
431
-
432
- onConnect: async (socket, ctx) => {
433
- const ws = app.getWebSocket()!;
434
- const gameId = ctx.query.game as string;
435
-
436
- if (!gameId) {
437
- socket.send(JSON.stringify({ type: 'error', message: 'Game ID required' }));
438
- socket.close();
439
- return;
440
- }
441
-
442
- // Join game room
443
- ws.joinRoom(`game:${gameId}`, socket);
444
-
445
- // Notify other players
446
- ws.broadcast(`game:${gameId}`, {
447
- type: 'player_joined',
448
- player: ctx.user
449
- });
450
-
451
- // Send current game state
452
- const gameState = await getGameState(gameId);
453
- socket.send(JSON.stringify({
454
- type: 'game_state',
455
- state: gameState
456
- }));
457
- },
458
-
459
- onMessage: async (socket, message, ctx) => {
460
- const ws = app.getWebSocket()!;
461
- const gameId = ctx.query.game as string;
462
-
463
- switch (message.type) {
464
- case 'move':
465
- const result = await processMove(gameId, ctx.user.id, message.move);
466
- ws.broadcast(`game:${gameId}`, {
467
- type: 'move_made',
468
- player: ctx.user.id,
469
- move: message.move,
470
- result
471
- });
472
- break;
473
-
474
- case 'chat':
475
- ws.broadcast(`game:${gameId}`, {
476
- type: 'game_chat',
477
- player: ctx.user.name,
478
- text: message.text
479
- });
480
- break;
481
- }
482
- },
483
-
484
- onClose: async (socket, ctx) => {
485
- const ws = app.getWebSocket()!;
486
- const gameId = ctx.query.game as string;
487
-
488
- ws.broadcast(`game:${gameId}`, {
489
- type: 'player_left',
490
- player: ctx.user
491
- });
492
- }
493
- });
494
- ```
495
-
496
- ## Client-Side Examples
497
-
498
- ### Browser (Vanilla JavaScript)
499
-
500
- ```javascript
501
- // Basic connection
502
- const ws = new WebSocket('ws://localhost:3000/ws/chat?token=myname');
503
-
504
- ws.onopen = () => {
505
- console.log('Connected!');
506
- ws.send(JSON.stringify({ type: 'chat', text: 'Hello!' }));
507
- };
508
-
509
- ws.onmessage = (event) => {
510
- const data = JSON.parse(event.data);
511
- console.log('Received:', data);
512
- };
513
-
514
- ws.onclose = () => {
515
- console.log('Disconnected');
516
- };
517
-
518
- ws.onerror = (error) => {
519
- console.error('WebSocket error:', error);
520
- };
521
- ```
522
-
523
- ### React Hook
524
-
525
- ```typescript
526
- import { useEffect, useState, useCallback, useRef } from 'react';
527
-
528
- function useWebSocket(url: string) {
529
- const [messages, setMessages] = useState<any[]>([]);
530
- const [isConnected, setIsConnected] = useState(false);
531
- const wsRef = useRef<WebSocket | null>(null);
532
-
533
- useEffect(() => {
534
- const ws = new WebSocket(url);
535
- wsRef.current = ws;
536
-
537
- ws.onopen = () => setIsConnected(true);
538
- ws.onclose = () => setIsConnected(false);
539
- ws.onmessage = (event) => {
540
- const data = JSON.parse(event.data);
541
- setMessages(prev => [...prev, data]);
542
- };
543
-
544
- return () => ws.close();
545
- }, [url]);
546
-
547
- const send = useCallback((data: any) => {
548
- wsRef.current?.send(JSON.stringify(data));
549
- }, []);
550
-
551
- return { messages, isConnected, send };
552
- }
553
-
554
- // Usage
555
- function ChatComponent() {
556
- const { messages, isConnected, send } = useWebSocket('ws://localhost:3000/ws/chat?token=user1');
557
-
558
- return (
559
- <div>
560
- <p>Status: {isConnected ? 'Connected' : 'Disconnected'}</p>
561
- <button onClick={() => send({ type: 'chat', text: 'Hello!' })}>
562
- Send
563
- </button>
564
- <ul>
565
- {messages.map((msg, i) => (
566
- <li key={i}>{JSON.stringify(msg)}</li>
567
- ))}
568
- </ul>
569
- </div>
570
- );
571
- }
572
- ```
573
-
574
- ### Node.js Client
575
-
576
- ```typescript
577
- import WebSocket from 'ws';
578
-
579
- const ws = new WebSocket('ws://localhost:3000/ws/chat?token=bot');
580
-
581
- ws.on('open', () => {
582
- console.log('Connected to server');
583
- ws.send(JSON.stringify({ type: 'chat', text: 'Bot is online!' }));
584
- });
585
-
586
- ws.on('message', (data) => {
587
- const message = JSON.parse(data.toString());
588
- console.log('Received:', message);
589
-
590
- // Auto-reply to messages
591
- if (message.type === 'chat' && message.user !== 'bot') {
592
- ws.send(JSON.stringify({
593
- type: 'chat',
594
- text: `You said: ${message.text}`
595
- }));
596
- }
597
- });
598
-
599
- ws.on('close', () => {
600
- console.log('Disconnected');
601
- });
602
- ```
603
-
604
- ## Error Handling
605
-
606
- ```typescript
607
- app.ws('/ws/robust', {
608
- auth: async (ctx) => {
609
- try {
610
- const token = ctx.query.token as string;
611
- if (!token) {
612
- throw new Error('AUTH_REQUIRED');
613
- }
614
-
615
- const user = await verifyToken(token);
616
- if (!user) {
617
- throw new Error('INVALID_TOKEN');
618
- }
619
-
620
- return user;
621
- } catch (error) {
622
- // Throwing here will reject the connection
623
- throw error;
624
- }
625
- },
626
-
627
- onConnect: async (socket, ctx) => {
628
- try {
629
- // Setup logic
630
- const ws = app.getWebSocket()!;
631
- ws.joinRoom('main', socket);
632
- } catch (error) {
633
- socket.send(JSON.stringify({
634
- type: 'error',
635
- message: 'Failed to setup connection'
636
- }));
637
- socket.close(1011, 'Setup failed');
638
- }
639
- },
640
-
641
- onMessage: async (socket, message, ctx) => {
642
- try {
643
- // Validate message
644
- if (!message.type) {
645
- throw new Error('Message type required');
646
- }
647
-
648
- // Process message
649
- await processMessage(message, ctx);
650
-
651
- } catch (error) {
652
- socket.send(JSON.stringify({
653
- type: 'error',
654
- message: error.message
655
- }));
656
- }
657
- },
658
-
659
- onError: async (socket, error, ctx) => {
660
- console.error('WebSocket Error:', {
661
- user: ctx.user?.id,
662
- error: error.message,
663
- stack: error.stack
664
- });
665
-
666
- // Optionally send error to client
667
- if (socket.readyState === WebSocket.OPEN) {
668
- socket.send(JSON.stringify({
669
- type: 'error',
670
- message: 'Internal server error'
671
- }));
672
- }
673
- }
674
- });
675
- ```
676
-
677
- ## Best Practices
678
-
679
- ### 1. Heartbeat/Ping-Pong
680
-
681
- ```typescript
682
- app.ws('/ws/with-heartbeat', {
683
- onConnect: async (socket, ctx) => {
684
- // Send ping every 30 seconds
685
- const pingInterval = setInterval(() => {
686
- if (socket.readyState === WebSocket.OPEN) {
687
- socket.ping();
688
- }
689
- }, 30000);
690
-
691
- // Store interval in context for cleanup
692
- ctx.metadata = { pingInterval };
693
- },
694
-
695
- onClose: async (socket, ctx) => {
696
- // Clear ping interval
697
- clearInterval(ctx.metadata?.pingInterval);
698
- }
699
- });
700
- ```
701
-
702
- ### 2. Rate Limiting
703
-
704
- ```typescript
705
- const messageCount = new Map<string, number>();
706
-
707
- app.ws('/ws/rate-limited', {
708
- auth: async (ctx) => {
709
- return { id: ctx.query.userId as string };
710
- },
711
-
712
- onMessage: async (socket, message, ctx) => {
713
- const userId = ctx.user.id;
714
- const count = (messageCount.get(userId) || 0) + 1;
715
- messageCount.set(userId, count);
716
-
717
- // Reset count every minute
718
- setTimeout(() => {
719
- messageCount.set(userId, Math.max(0, (messageCount.get(userId) || 0) - 1));
720
- }, 60000);
721
-
722
- // Limit: 100 messages per minute
723
- if (count > 100) {
724
- socket.send(JSON.stringify({
725
- type: 'error',
726
- message: 'Rate limit exceeded'
727
- }));
728
- return;
729
- }
730
-
731
- // Process message...
732
- }
733
- });
734
- ```
735
-
736
- ### 3. Message Validation
737
-
738
- ```typescript
739
- import { z } from 'zod';
740
-
741
- const ChatMessageSchema = z.object({
742
- type: z.literal('chat'),
743
- room: z.string().optional(),
744
- text: z.string().min(1).max(1000)
745
- });
746
-
747
- const JoinRoomSchema = z.object({
748
- type: z.literal('join_room'),
749
- room: z.string().min(1).max(50)
750
- });
751
-
752
- const MessageSchema = z.discriminatedUnion('type', [
753
- ChatMessageSchema,
754
- JoinRoomSchema
755
- ]);
756
-
757
- app.ws('/ws/validated', {
758
- onMessage: async (socket, message, ctx) => {
759
- const result = MessageSchema.safeParse(message);
760
-
761
- if (!result.success) {
762
- socket.send(JSON.stringify({
763
- type: 'error',
764
- message: 'Invalid message format',
765
- errors: result.error.errors
766
- }));
767
- return;
768
- }
769
-
770
- const validatedMessage = result.data;
771
-
772
- // Now TypeScript knows the exact type
773
- switch (validatedMessage.type) {
774
- case 'chat':
775
- // validatedMessage.text is guaranteed to exist
776
- break;
777
- case 'join_room':
778
- // validatedMessage.room is guaranteed to exist
779
- break;
780
- }
781
- }
782
- });
783
- ```
784
-
785
- ## WebSocket vs HTTP
786
-
787
- | Feature | WebSocket | HTTP |
788
- |---------|-----------|------|
789
- | Connection | Persistent | Request/Response |
790
- | Direction | Bidirectional | Client → Server |
791
- | Overhead | Low (after handshake) | High (headers per request) |
792
- | Use Case | Real-time, streaming | CRUD operations |
793
- | Scaling | More complex | Easier |
794
-
795
- **Gunakan WebSocket untuk:**
796
- - Chat applications
797
- - Live notifications
798
- - Real-time dashboards
799
- - Multiplayer games
800
- - Collaborative editing
801
- - Live streaming data
802
-
803
- **Gunakan HTTP untuk:**
804
- - CRUD operations
805
- - File uploads
806
- - Authentication endpoints
807
- - One-time requests
808
-
809
- ---
810
-
811
- **Related:**
812
- - [03-routing.md](./03-routing.md) - HTTP Routing
813
- - [19-class-based-routing.md](./19-class-based-routing.md) - Class-based Routes