@engjts/nexus 0.1.7 → 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 (259) hide show
  1. package/dist/advanced/playground/generatePlaygroundHTML.d.ts.map +1 -1
  2. package/dist/advanced/playground/generatePlaygroundHTML.js +107 -0
  3. package/dist/advanced/playground/generatePlaygroundHTML.js.map +1 -1
  4. package/dist/advanced/playground/playground.d.ts +19 -0
  5. package/dist/advanced/playground/playground.d.ts.map +1 -1
  6. package/dist/advanced/playground/playground.js +70 -0
  7. package/dist/advanced/playground/playground.js.map +1 -1
  8. package/dist/advanced/playground/types.d.ts +20 -0
  9. package/dist/advanced/playground/types.d.ts.map +1 -1
  10. package/dist/core/application.d.ts +14 -0
  11. package/dist/core/application.d.ts.map +1 -1
  12. package/dist/core/application.js +173 -71
  13. package/dist/core/application.js.map +1 -1
  14. package/dist/core/context-pool.d.ts +2 -13
  15. package/dist/core/context-pool.d.ts.map +1 -1
  16. package/dist/core/context-pool.js +7 -45
  17. package/dist/core/context-pool.js.map +1 -1
  18. package/dist/core/context.d.ts +108 -5
  19. package/dist/core/context.d.ts.map +1 -1
  20. package/dist/core/context.js +449 -53
  21. package/dist/core/context.js.map +1 -1
  22. package/dist/core/index.d.ts +1 -0
  23. package/dist/core/index.d.ts.map +1 -1
  24. package/dist/core/index.js +9 -1
  25. package/dist/core/index.js.map +1 -1
  26. package/dist/core/middleware.d.ts +6 -0
  27. package/dist/core/middleware.d.ts.map +1 -1
  28. package/dist/core/middleware.js +83 -84
  29. package/dist/core/middleware.js.map +1 -1
  30. package/dist/core/performance/fast-json.d.ts +149 -0
  31. package/dist/core/performance/fast-json.d.ts.map +1 -0
  32. package/dist/core/performance/fast-json.js +473 -0
  33. package/dist/core/performance/fast-json.js.map +1 -0
  34. package/dist/core/router/file-router.d.ts +20 -7
  35. package/dist/core/router/file-router.d.ts.map +1 -1
  36. package/dist/core/router/file-router.js +41 -13
  37. package/dist/core/router/file-router.js.map +1 -1
  38. package/dist/core/router/index.d.ts +6 -0
  39. package/dist/core/router/index.d.ts.map +1 -1
  40. package/dist/core/router/index.js +33 -6
  41. package/dist/core/router/index.js.map +1 -1
  42. package/dist/core/router/radix-tree.d.ts +4 -1
  43. package/dist/core/router/radix-tree.d.ts.map +1 -1
  44. package/dist/core/router/radix-tree.js +7 -3
  45. package/dist/core/router/radix-tree.js.map +1 -1
  46. package/dist/core/serializer.d.ts +251 -0
  47. package/dist/core/serializer.d.ts.map +1 -0
  48. package/dist/core/serializer.js +290 -0
  49. package/dist/core/serializer.js.map +1 -0
  50. package/dist/core/types.d.ts +39 -1
  51. package/dist/core/types.d.ts.map +1 -1
  52. package/dist/core/types.js.map +1 -1
  53. package/dist/index.d.ts +1 -0
  54. package/dist/index.d.ts.map +1 -1
  55. package/dist/index.js +12 -2
  56. package/dist/index.js.map +1 -1
  57. package/package.json +3 -1
  58. package/documentation/01-getting-started.md +0 -240
  59. package/documentation/02-context.md +0 -335
  60. package/documentation/03-routing.md +0 -397
  61. package/documentation/04-middleware.md +0 -483
  62. package/documentation/05-validation.md +0 -514
  63. package/documentation/06-error-handling.md +0 -465
  64. package/documentation/07-performance.md +0 -364
  65. package/documentation/08-adapters.md +0 -470
  66. package/documentation/09-api-reference.md +0 -548
  67. package/documentation/10-examples.md +0 -582
  68. package/documentation/11-deployment.md +0 -477
  69. package/documentation/12-sentry.md +0 -620
  70. package/documentation/13-sentry-data-storage.md +0 -996
  71. package/documentation/14-sentry-data-reference.md +0 -457
  72. package/documentation/15-sentry-summary.md +0 -409
  73. package/documentation/16-alerts-system.md +0 -745
  74. package/documentation/17-alert-adapters.md +0 -696
  75. package/documentation/18-alerts-implementation-summary.md +0 -385
  76. package/documentation/19-class-based-routing.md +0 -840
  77. package/documentation/20-websocket-realtime.md +0 -813
  78. package/documentation/21-cache-system.md +0 -510
  79. package/documentation/22-job-queue.md +0 -772
  80. package/documentation/23-sentry-plugin.md +0 -551
  81. package/documentation/24-testing-utilities.md +0 -1287
  82. package/documentation/25-api-versioning.md +0 -533
  83. package/documentation/26-context-store.md +0 -607
  84. package/documentation/27-dependency-injection.md +0 -329
  85. package/documentation/28-lifecycle-hooks.md +0 -521
  86. package/documentation/29-package-structure.md +0 -196
  87. package/documentation/30-plugin-system.md +0 -414
  88. package/documentation/31-jwt-authentication.md +0 -597
  89. package/documentation/32-cli.md +0 -268
  90. package/documentation/ALERTS-COMPLETE-SUMMARY.md +0 -429
  91. package/documentation/ALERTS-INDEX.md +0 -330
  92. package/documentation/ALERTS-QUICK-REFERENCE.md +0 -286
  93. package/documentation/README.md +0 -178
  94. package/documentation/index.html +0 -34
  95. package/modern_framework_paper.md +0 -1870
  96. package/public/css/style.css +0 -87
  97. package/public/index.html +0 -34
  98. package/public/js/app.js +0 -27
  99. package/src/advanced/cache/InMemoryCacheStore.ts +0 -68
  100. package/src/advanced/cache/MultiTierCache.ts +0 -194
  101. package/src/advanced/cache/RedisCacheStore.ts +0 -341
  102. package/src/advanced/cache/index.ts +0 -5
  103. package/src/advanced/cache/types.ts +0 -40
  104. package/src/advanced/graphql/SimpleDataLoader.ts +0 -42
  105. package/src/advanced/graphql/index.ts +0 -22
  106. package/src/advanced/graphql/server.ts +0 -252
  107. package/src/advanced/graphql/types.ts +0 -42
  108. package/src/advanced/jobs/InMemoryQueueStore.ts +0 -68
  109. package/src/advanced/jobs/JobQueue.ts +0 -556
  110. package/src/advanced/jobs/RedisQueueStore.ts +0 -367
  111. package/src/advanced/jobs/index.ts +0 -5
  112. package/src/advanced/jobs/types.ts +0 -70
  113. package/src/advanced/observability/APMManager.ts +0 -163
  114. package/src/advanced/observability/AlertManager.ts +0 -109
  115. package/src/advanced/observability/MetricRegistry.ts +0 -151
  116. package/src/advanced/observability/ObservabilityCenter.ts +0 -304
  117. package/src/advanced/observability/StructuredLogger.ts +0 -154
  118. package/src/advanced/observability/TracingManager.ts +0 -117
  119. package/src/advanced/observability/adapters.ts +0 -304
  120. package/src/advanced/observability/createObservabilityMiddleware.ts +0 -63
  121. package/src/advanced/observability/index.ts +0 -11
  122. package/src/advanced/observability/types.ts +0 -174
  123. package/src/advanced/playground/extractPathParams.ts +0 -6
  124. package/src/advanced/playground/generateFieldExample.ts +0 -31
  125. package/src/advanced/playground/generatePlaygroundHTML.ts +0 -1849
  126. package/src/advanced/playground/generateSummary.ts +0 -19
  127. package/src/advanced/playground/getTagFromPath.ts +0 -9
  128. package/src/advanced/playground/index.ts +0 -8
  129. package/src/advanced/playground/playground.ts +0 -170
  130. package/src/advanced/playground/types.ts +0 -20
  131. package/src/advanced/playground/zodToExample.ts +0 -16
  132. package/src/advanced/playground/zodToParams.ts +0 -15
  133. package/src/advanced/postman/buildAuth.ts +0 -31
  134. package/src/advanced/postman/buildBody.ts +0 -15
  135. package/src/advanced/postman/buildQueryParams.ts +0 -27
  136. package/src/advanced/postman/buildRequestItem.ts +0 -36
  137. package/src/advanced/postman/buildResponses.ts +0 -11
  138. package/src/advanced/postman/buildUrl.ts +0 -33
  139. package/src/advanced/postman/capitalize.ts +0 -4
  140. package/src/advanced/postman/generateCollection.ts +0 -59
  141. package/src/advanced/postman/generateEnvironment.ts +0 -34
  142. package/src/advanced/postman/generateExampleFromZod.ts +0 -21
  143. package/src/advanced/postman/generateFieldExample.ts +0 -45
  144. package/src/advanced/postman/generateName.ts +0 -20
  145. package/src/advanced/postman/generateUUID.ts +0 -11
  146. package/src/advanced/postman/getTagFromPath.ts +0 -10
  147. package/src/advanced/postman/index.ts +0 -28
  148. package/src/advanced/postman/postman.ts +0 -156
  149. package/src/advanced/postman/slugify.ts +0 -7
  150. package/src/advanced/postman/types.ts +0 -140
  151. package/src/advanced/realtime/index.ts +0 -18
  152. package/src/advanced/realtime/websocket.ts +0 -231
  153. package/src/advanced/sentry/index.ts +0 -1236
  154. package/src/advanced/sentry/types.ts +0 -355
  155. package/src/advanced/static/generateDirectoryListing.ts +0 -47
  156. package/src/advanced/static/generateETag.ts +0 -7
  157. package/src/advanced/static/getMimeType.ts +0 -9
  158. package/src/advanced/static/index.ts +0 -32
  159. package/src/advanced/static/isSafePath.ts +0 -13
  160. package/src/advanced/static/publicDir.ts +0 -21
  161. package/src/advanced/static/serveStatic.ts +0 -225
  162. package/src/advanced/static/spa.ts +0 -24
  163. package/src/advanced/static/types.ts +0 -159
  164. package/src/advanced/swagger/SwaggerGenerator.ts +0 -66
  165. package/src/advanced/swagger/buildOperation.ts +0 -61
  166. package/src/advanced/swagger/buildParameters.ts +0 -61
  167. package/src/advanced/swagger/buildRequestBody.ts +0 -21
  168. package/src/advanced/swagger/buildResponses.ts +0 -54
  169. package/src/advanced/swagger/capitalize.ts +0 -5
  170. package/src/advanced/swagger/convertPath.ts +0 -9
  171. package/src/advanced/swagger/createSwagger.ts +0 -12
  172. package/src/advanced/swagger/generateOperationId.ts +0 -21
  173. package/src/advanced/swagger/generateSpec.ts +0 -105
  174. package/src/advanced/swagger/generateSummary.ts +0 -24
  175. package/src/advanced/swagger/generateSwaggerUI.ts +0 -70
  176. package/src/advanced/swagger/generateThemeCss.ts +0 -53
  177. package/src/advanced/swagger/index.ts +0 -25
  178. package/src/advanced/swagger/swagger.ts +0 -237
  179. package/src/advanced/swagger/types.ts +0 -206
  180. package/src/advanced/swagger/zodFieldToOpenAPI.ts +0 -94
  181. package/src/advanced/swagger/zodSchemaToOpenAPI.ts +0 -50
  182. package/src/advanced/swagger/zodToOpenAPI.ts +0 -22
  183. package/src/advanced/testing/factory.ts +0 -509
  184. package/src/advanced/testing/harness.ts +0 -612
  185. package/src/advanced/testing/index.ts +0 -430
  186. package/src/advanced/testing/load-test.ts +0 -618
  187. package/src/advanced/testing/mock-server.ts +0 -498
  188. package/src/advanced/testing/mock.ts +0 -670
  189. package/src/cli/bin.ts +0 -9
  190. package/src/cli/cli.ts +0 -158
  191. package/src/cli/commands/add.ts +0 -178
  192. package/src/cli/commands/build.ts +0 -73
  193. package/src/cli/commands/create.ts +0 -166
  194. package/src/cli/commands/dev.ts +0 -85
  195. package/src/cli/commands/generate.ts +0 -99
  196. package/src/cli/commands/help.ts +0 -95
  197. package/src/cli/commands/init.ts +0 -91
  198. package/src/cli/commands/version.ts +0 -38
  199. package/src/cli/index.ts +0 -6
  200. package/src/cli/templates/generators.ts +0 -359
  201. package/src/cli/templates/index.ts +0 -680
  202. package/src/cli/utils/exec.ts +0 -52
  203. package/src/cli/utils/file-system.ts +0 -78
  204. package/src/cli/utils/logger.ts +0 -111
  205. package/src/core/adapter.ts +0 -88
  206. package/src/core/application.ts +0 -1335
  207. package/src/core/context-pool.ts +0 -127
  208. package/src/core/context.ts +0 -412
  209. package/src/core/index.ts +0 -80
  210. package/src/core/middleware.ts +0 -262
  211. package/src/core/performance/buffer-pool.ts +0 -108
  212. package/src/core/performance/middleware-optimizer.ts +0 -162
  213. package/src/core/plugin/PluginManager.ts +0 -435
  214. package/src/core/plugin/builder.ts +0 -358
  215. package/src/core/plugin/index.ts +0 -50
  216. package/src/core/plugin/types.ts +0 -214
  217. package/src/core/router/file-router.ts +0 -594
  218. package/src/core/router/index.ts +0 -227
  219. package/src/core/router/radix-tree.ts +0 -226
  220. package/src/core/store/index.ts +0 -30
  221. package/src/core/store/registry.ts +0 -178
  222. package/src/core/store/request-store.ts +0 -240
  223. package/src/core/store/types.ts +0 -233
  224. package/src/core/types.ts +0 -574
  225. package/src/database/adapter.ts +0 -35
  226. package/src/database/adapters/index.ts +0 -1
  227. package/src/database/adapters/mysql.ts +0 -669
  228. package/src/database/database.ts +0 -70
  229. package/src/database/dialect.ts +0 -388
  230. package/src/database/index.ts +0 -12
  231. package/src/database/migrations.ts +0 -86
  232. package/src/database/optimizer.ts +0 -125
  233. package/src/database/query-builder.ts +0 -404
  234. package/src/database/realtime.ts +0 -53
  235. package/src/database/schema.ts +0 -71
  236. package/src/database/transactions.ts +0 -56
  237. package/src/database/types.ts +0 -87
  238. package/src/deployment/cluster.ts +0 -471
  239. package/src/deployment/config.ts +0 -454
  240. package/src/deployment/docker.ts +0 -599
  241. package/src/deployment/graceful-shutdown.ts +0 -373
  242. package/src/deployment/index.ts +0 -56
  243. package/src/index.ts +0 -264
  244. package/src/security/adapter.ts +0 -318
  245. package/src/security/auth/JWTPlugin.ts +0 -234
  246. package/src/security/auth/JWTProvider.ts +0 -316
  247. package/src/security/auth/adapter.ts +0 -12
  248. package/src/security/auth/jwt.ts +0 -234
  249. package/src/security/auth/middleware.ts +0 -188
  250. package/src/security/csrf.ts +0 -220
  251. package/src/security/headers.ts +0 -108
  252. package/src/security/index.ts +0 -60
  253. package/src/security/rate-limit/adapter.ts +0 -7
  254. package/src/security/rate-limit/memory.ts +0 -108
  255. package/src/security/rate-limit/middleware.ts +0 -181
  256. package/src/security/sanitization.ts +0 -75
  257. package/src/security/types.ts +0 -240
  258. package/src/security/utils.ts +0 -52
  259. 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