0
# Middleware System
1
2
Redux-style middleware system for intercepting and processing VK Bridge communications, enabling logging, data transformation, error handling, and custom request/response processing.
3
4
## Capabilities
5
6
### Apply Middleware
7
8
Create an enhanced VK Bridge instance with middleware applied to the send method.
9
10
```typescript { .api }
11
/**
12
* Apply middleware to VK Bridge instance
13
* Creates enhanced bridge with middleware chain applied to send method
14
* @param middlewares - Array of middleware functions to apply
15
* @returns Function that takes bridge and returns enhanced bridge
16
*/
17
function applyMiddleware(
18
...middlewares: Array<Middleware | undefined | null>
19
): (bridge: VKBridge) => VKBridge;
20
21
type Middleware<S extends VKBridgeSend = VKBridgeSend> = (
22
api: MiddlewareAPI<S>
23
) => (next: S) => S;
24
25
interface MiddlewareAPI<S extends VKBridgeSend = VKBridgeSend> {
26
/** Send function for making bridge calls within middleware */
27
send: S;
28
/** Subscribe function for event listening within middleware */
29
subscribe(listener: VKBridgeSubscribeHandler): void;
30
}
31
32
type VKBridgeSend = <K extends AnyRequestMethodName>(
33
method: K,
34
props?: RequestProps<K> & RequestIdProp
35
) => Promise<K extends AnyReceiveMethodName ? ReceiveData<K> : void>;
36
```
37
38
**Usage Examples:**
39
40
```typescript
41
import bridge, { applyMiddleware } from "@vkontakte/vk-bridge";
42
43
// Create logger middleware
44
const loggerMiddleware = ({ send, subscribe }) => (next) => async (method, props) => {
45
console.log('→', method, props);
46
const startTime = Date.now();
47
48
try {
49
const result = await next(method, props);
50
const duration = Date.now() - startTime;
51
console.log('✓', method, `${duration}ms`, result);
52
return result;
53
} catch (error) {
54
const duration = Date.now() - startTime;
55
console.error('✗', method, `${duration}ms`, error);
56
throw error;
57
}
58
};
59
60
// Apply middleware and create enhanced bridge
61
const enhancedBridge = applyMiddleware(loggerMiddleware)(bridge);
62
63
// Use enhanced bridge normally
64
await enhancedBridge.send('VKWebAppInit');
65
await enhancedBridge.send('VKWebAppGetUserInfo');
66
```
67
68
### Built-in Middleware Examples
69
70
#### Logging Middleware
71
72
```typescript { .api }
73
interface LoggerMiddleware {
74
/** Log all requests and responses with timing */
75
(api: MiddlewareAPI): (next: VKBridgeSend) => VKBridgeSend;
76
}
77
```
78
79
**Usage Examples:**
80
81
```typescript
82
// Basic logger
83
const basicLogger = ({ send, subscribe }) => (next) => async (method, props) => {
84
console.log(`Sending: ${method}`, props);
85
const result = await next(method, props);
86
console.log(`Received: ${method}`, result);
87
return result;
88
};
89
90
// Advanced logger with timing and error tracking
91
const advancedLogger = ({ send, subscribe }) => (next) => async (method, props) => {
92
const requestId = props?.request_id || Math.random().toString(36).substr(2, 9);
93
const startTime = performance.now();
94
95
console.group(`🔄 ${method} [${requestId}]`);
96
console.log('Parameters:', props);
97
98
try {
99
const result = await next(method, props);
100
const duration = performance.now() - startTime;
101
102
console.log('✅ Success:', result);
103
console.log(`⏱️ Duration: ${duration.toFixed(2)}ms`);
104
console.groupEnd();
105
106
return result;
107
} catch (error) {
108
const duration = performance.now() - startTime;
109
110
console.error('❌ Error:', error);
111
console.log(`⏱️ Duration: ${duration.toFixed(2)}ms`);
112
console.groupEnd();
113
114
throw error;
115
}
116
};
117
118
const loggedBridge = applyMiddleware(advancedLogger)(bridge);
119
```
120
121
#### Error Handling Middleware
122
123
```typescript { .api }
124
interface ErrorHandlerMiddleware {
125
/** Handle and transform errors before they reach the application */
126
(api: MiddlewareAPI): (next: VKBridgeSend) => VKBridgeSend;
127
}
128
```
129
130
**Usage Examples:**
131
132
```typescript
133
// Retry middleware for network errors
134
const retryMiddleware = ({ send, subscribe }) => (next) => async (method, props) => {
135
const maxRetries = 3;
136
let lastError;
137
138
for (let attempt = 1; attempt <= maxRetries; attempt++) {
139
try {
140
return await next(method, props);
141
} catch (error) {
142
lastError = error;
143
144
// Only retry on network/client errors
145
if (error.error_type === 'client_error' && attempt < maxRetries) {
146
console.warn(`Retry ${attempt}/${maxRetries} for ${method}`);
147
await new Promise(resolve => setTimeout(resolve, 1000 * attempt));
148
continue;
149
}
150
151
throw error;
152
}
153
}
154
155
throw lastError;
156
};
157
158
// Global error handler
159
const errorHandlerMiddleware = ({ send, subscribe }) => (next) => async (method, props) => {
160
try {
161
return await next(method, props);
162
} catch (error) {
163
// Log error for analytics
164
console.error('Bridge error:', { method, props, error });
165
166
// Transform specific errors
167
if (error.error_type === 'auth_error' && error.error_data.error_code === 4) {
168
throw new Error('User cancelled authorization');
169
}
170
171
if (error.error_type === 'client_error' && error.error_data.error_code === 1) {
172
throw new Error('Method not supported on this platform');
173
}
174
175
// Re-throw original error
176
throw error;
177
}
178
};
179
180
const robustBridge = applyMiddleware(retryMiddleware, errorHandlerMiddleware)(bridge);
181
```
182
183
#### Data Transformation Middleware
184
185
```typescript { .api }
186
interface DataTransformMiddleware {
187
/** Transform request/response data */
188
(api: MiddlewareAPI): (next: VKBridgeSend) => VKBridgeSend;
189
}
190
```
191
192
**Usage Examples:**
193
194
```typescript
195
// Request/response transformer
196
const transformerMiddleware = ({ send, subscribe }) => (next) => async (method, props) => {
197
// Transform outgoing requests
198
let transformedProps = props;
199
200
if (method === 'VKWebAppGetAuthToken' && props) {
201
// Ensure scope is always a string
202
transformedProps = {
203
...props,
204
scope: Array.isArray(props.scope) ? props.scope.join(',') : props.scope
205
};
206
}
207
208
const result = await next(method, transformedProps);
209
210
// Transform incoming responses
211
if (method === 'VKWebAppGetUserInfo' && result) {
212
return {
213
...result,
214
full_name: `${result.first_name} ${result.last_name}`,
215
avatar: result.photo_200 || result.photo_100
216
};
217
}
218
219
return result;
220
};
221
222
// Data normalization middleware
223
const normalizerMiddleware = ({ send, subscribe }) => (next) => async (method, props) => {
224
const result = await next(method, props);
225
226
// Normalize timestamps to Date objects
227
if (result && typeof result === 'object') {
228
const normalizedResult = { ...result };
229
230
// Convert known timestamp fields
231
const timestampFields = ['vk_ts', 'timestamp', 'date', 'created', 'updated'];
232
timestampFields.forEach(field => {
233
if (field in normalizedResult && typeof normalizedResult[field] === 'number') {
234
normalizedResult[`${field}_date`] = new Date(normalizedResult[field] * 1000);
235
}
236
});
237
238
return normalizedResult;
239
}
240
241
return result;
242
};
243
244
const transformedBridge = applyMiddleware(transformerMiddleware, normalizerMiddleware)(bridge);
245
```
246
247
#### Analytics Middleware
248
249
```typescript { .api }
250
interface AnalyticsMiddleware {
251
/** Track bridge usage for analytics */
252
(api: MiddlewareAPI): (next: VKBridgeSend) => VKBridgeSend;
253
}
254
```
255
256
**Usage Examples:**
257
258
```typescript
259
// Usage tracking middleware
260
const analyticsMiddleware = ({ send, subscribe }) => (next) => async (method, props) => {
261
const startTime = Date.now();
262
263
try {
264
const result = await next(method, props);
265
const duration = Date.now() - startTime;
266
267
// Track successful usage
268
trackEvent('bridge_method_success', {
269
method,
270
duration,
271
platform: bridge.isWebView() ? 'webview' : 'web',
272
timestamp: Date.now()
273
});
274
275
return result;
276
} catch (error) {
277
const duration = Date.now() - startTime;
278
279
// Track errors
280
trackEvent('bridge_method_error', {
281
method,
282
duration,
283
error_type: error.error_type,
284
error_code: error.error_data?.error_code,
285
platform: bridge.isWebView() ? 'webview' : 'web',
286
timestamp: Date.now()
287
});
288
289
throw error;
290
}
291
};
292
293
// Performance monitoring
294
const performanceMiddleware = ({ send, subscribe }) => (next) => async (method, props) => {
295
const startTime = performance.now();
296
297
try {
298
const result = await next(method, props);
299
const duration = performance.now() - startTime;
300
301
// Log slow operations
302
if (duration > 1000) {
303
console.warn(`Slow bridge operation: ${method} took ${duration.toFixed(2)}ms`);
304
}
305
306
// Send to monitoring service
307
if (window.performance && window.performance.mark) {
308
window.performance.mark(`bridge-${method}-start`);
309
window.performance.mark(`bridge-${method}-end`);
310
window.performance.measure(`bridge-${method}`, `bridge-${method}-start`, `bridge-${method}-end`);
311
}
312
313
return result;
314
} catch (error) {
315
const duration = performance.now() - startTime;
316
console.error(`Bridge error in ${method} after ${duration.toFixed(2)}ms:`, error);
317
throw error;
318
}
319
};
320
321
const monitoredBridge = applyMiddleware(analyticsMiddleware, performanceMiddleware)(bridge);
322
```
323
324
### Multiple Middleware Composition
325
326
```typescript { .api }
327
/**
328
* Compose multiple middleware functions
329
* Middleware is applied in order: first middleware wraps the second, etc.
330
*/
331
function composeMiddleware(...middlewares: Middleware[]): (bridge: VKBridge) => VKBridge;
332
```
333
334
**Usage Examples:**
335
336
```typescript
337
// Create comprehensive middleware stack
338
const middlewareStack = [
339
// First: Log all requests (outermost)
340
loggerMiddleware,
341
342
// Second: Handle authentication errors
343
authErrorHandlerMiddleware,
344
345
// Third: Retry failed requests
346
retryMiddleware,
347
348
// Fourth: Transform data
349
transformerMiddleware,
350
351
// Fifth: Track analytics (innermost)
352
analyticsMiddleware
353
];
354
355
// Apply all middleware
356
const fullBridge = applyMiddleware(...middlewareStack)(bridge);
357
358
// Alternative: Step by step application
359
const step1 = applyMiddleware(loggerMiddleware)(bridge);
360
const step2 = applyMiddleware(authErrorHandlerMiddleware)(step1);
361
const step3 = applyMiddleware(retryMiddleware)(step2);
362
const fullBridge2 = applyMiddleware(transformerMiddleware, analyticsMiddleware)(step3);
363
364
// Use the enhanced bridge
365
await fullBridge.send('VKWebAppInit');
366
await fullBridge.send('VKWebAppGetUserInfo');
367
```
368
369
### Conditional Middleware
370
371
```typescript
372
// Development vs production middleware
373
const createBridge = (isDevelopment: boolean) => {
374
const middlewares = [];
375
376
if (isDevelopment) {
377
middlewares.push(advancedLogger);
378
middlewares.push(performanceMiddleware);
379
} else {
380
middlewares.push(basicLogger);
381
middlewares.push(analyticsMiddleware);
382
}
383
384
// Always apply error handling
385
middlewares.push(errorHandlerMiddleware);
386
387
return applyMiddleware(...middlewares)(bridge);
388
};
389
390
const developmentBridge = createBridge(true);
391
const productionBridge = createBridge(false);
392
393
// Feature-based middleware
394
const createFeatureBridge = (features: { analytics?: boolean; retry?: boolean; logging?: boolean }) => {
395
const middlewares = [];
396
397
if (features.logging) {
398
middlewares.push(loggerMiddleware);
399
}
400
401
if (features.retry) {
402
middlewares.push(retryMiddleware);
403
}
404
405
if (features.analytics) {
406
middlewares.push(analyticsMiddleware);
407
}
408
409
return middlewares.length > 0 ? applyMiddleware(...middlewares)(bridge) : bridge;
410
};
411
```
412
413
## Advanced Middleware Patterns
414
415
### Middleware with State
416
417
```typescript
418
// Middleware with internal state
419
const createCacheMiddleware = (ttl: number = 60000) => {
420
const cache = new Map();
421
422
return ({ send, subscribe }) => (next) => async (method, props) => {
423
// Only cache certain methods
424
const cacheableMethods = ['VKWebAppGetUserInfo', 'VKWebAppGetConfig'];
425
426
if (!cacheableMethods.includes(method)) {
427
return next(method, props);
428
}
429
430
const cacheKey = `${method}:${JSON.stringify(props)}`;
431
const cached = cache.get(cacheKey);
432
433
if (cached && Date.now() - cached.timestamp < ttl) {
434
console.log('Cache hit:', method);
435
return cached.data;
436
}
437
438
const result = await next(method, props);
439
cache.set(cacheKey, { data: result, timestamp: Date.now() });
440
441
return result;
442
};
443
};
444
445
const cachedBridge = applyMiddleware(createCacheMiddleware(30000))(bridge);
446
```
447
448
### Async Middleware
449
450
```typescript
451
// Middleware with async initialization
452
const createAuthMiddleware = async (authConfig) => {
453
const authToken = await getStoredAuthToken();
454
455
return ({ send, subscribe }) => (next) => async (method, props) => {
456
// Auto-inject auth token for API calls
457
if (method === 'VKWebAppCallAPIMethod' && authToken) {
458
const enhancedProps = {
459
...props,
460
params: {
461
...props.params,
462
access_token: authToken
463
}
464
};
465
return next(method, enhancedProps);
466
}
467
468
return next(method, props);
469
};
470
};
471
472
// Async bridge creation
473
async function createAuthenticatedBridge() {
474
const authMiddleware = await createAuthMiddleware(authConfig);
475
return applyMiddleware(authMiddleware)(bridge);
476
}
477
478
const authenticatedBridge = await createAuthenticatedBridge();
479
```