Documentation Index Fetch the complete documentation index at: https://mintlify.com/negezor/vk-io/llms.txt
Use this file to discover all available pages before exploring further.
Overview
VK-IO uses a middleware-based architecture for handling events. Middleware functions form a chain where each function can process the context and pass control to the next middleware.
Basic Middleware Concept
Middleware is a function that receives a context object and a next function:
import { MessageContext } from 'vk-io' ;
vk . updates . use ( async ( context : MessageContext , next ) => {
// Code before next() runs first
console . log ( 'Before next' );
// Pass control to next middleware
await next ();
// Code after next() runs after all subsequent middleware
console . log ( 'After next' );
});
Middleware Chain
Basic Chain
vk . updates . use ( async ( context , next ) => {
console . log ( 'Middleware 1: start' );
await next ();
console . log ( 'Middleware 1: end' );
});
vk . updates . use ( async ( context , next ) => {
console . log ( 'Middleware 2: start' );
await next ();
console . log ( 'Middleware 2: end' );
});
vk . updates . use ( async ( context , next ) => {
console . log ( 'Middleware 3: start' );
await next ();
console . log ( 'Middleware 3: end' );
});
// Output:
// Middleware 1: start
// Middleware 2: start
// Middleware 3: start
// Middleware 3: end
// Middleware 2: end
// Middleware 1: end
Stopping the Chain
Not calling next() stops the middleware chain:
vk . updates . use ( async ( context , next ) => {
// This middleware stops the chain for bots
if ( context . is ( 'message' ) && context . senderType === 'user' && context . senderId < 0 ) {
console . log ( 'Ignoring bot message' );
return ; // Don't call next()
}
await next ();
});
vk . updates . use ( async ( context ) => {
// This will only run for non-bot messages
console . log ( 'Processing message:' , context . text );
});
Common Middleware Patterns
Logging Middleware
vk . updates . use ( async ( context , next ) => {
const start = Date . now ();
console . log ( `[ ${ context . type } ] Event received` );
await next ();
const duration = Date . now () - start ;
console . log ( `[ ${ context . type } ] Processed in ${ duration } ms` );
});
Authentication Middleware
const ADMIN_IDS = [ 1 , 2 , 3 ];
vk . updates . use ( async ( context , next ) => {
if ( ! context . is ( 'message' )) {
return next ();
}
// Add isAdmin property to context
context . isAdmin = ADMIN_IDS . includes ( context . senderId );
await next ();
});
// Later in handlers
vk . updates . hear ( /admin command/ i , async ( context ) => {
if ( ! context . isAdmin ) {
return context . send ( 'Access denied' );
}
// Admin logic
});
Context Modification
Real example from VK-IO examples:
const { VK } = require ( 'vk-io' );
const { HearManager } = require ( '@vk-io/hear' );
const vk = new VK ({ token: process . env . TOKEN });
const hearManager = new HearManager ();
// Some users "database"
const users = new Map ([]);
vk . updates . on ( 'message_new' , ( context , next ) => {
let user = users . get ( context . senderId );
if ( ! user ) {
user = {
displayName: `User ${ context . senderId } ` ,
};
users . set ( context . senderId , user );
}
// Add user to context
context . user = user ;
// Add custom method
context . answer = ( text , params ) =>
context . send ( ` ${ context . user . displayName } , ${ text } ` , params );
return next ();
});
vk . updates . on ( 'message_new' , hearManager . middleware );
hearManager . hear ( /hello/ i , async context => {
await context . answer ( 'hello!' ); // Will send "User 1234, hello!"
});
hearManager . hear ( /set username ( . + ) / i , async context => {
const [, displayName ] = context . $match ;
context . user . displayName = displayName ;
await context . answer ( 'display name changed.' );
});
Rate Limiting
const rateLimits = new Map < number , number >();
const RATE_LIMIT = 5 ; // messages per minute
vk . updates . use ( async ( context , next ) => {
if ( ! context . is ( 'message' )) {
return next ();
}
const userId = context . senderId ;
const now = Date . now ();
const lastMessage = rateLimits . get ( userId ) || 0 ;
if ( now - lastMessage < 60000 / RATE_LIMIT ) {
console . log ( `Rate limit exceeded for user ${ userId } ` );
return ; // Don't call next()
}
rateLimits . set ( userId , now );
await next ();
});
Command Parser
interface CommandContext extends MessageContext {
command ?: string ;
args ?: string [];
}
vk . updates . use ( async ( context : CommandContext , next ) => {
if ( ! context . is ( 'message' ) || ! context . text ) {
return next ();
}
// Parse commands starting with /
if ( context . text . startsWith ( '/' )) {
const [ command , ... args ] = context . text . slice ( 1 ). split ( ' ' );
context . command = command . toLowerCase ();
context . args = args ;
}
await next ();
});
// Use the parsed command
vk . updates . use ( async ( context : CommandContext ) => {
if ( context . command === 'help' ) {
await context . send ( 'Available commands: /help, /start, /info' );
} else if ( context . command === 'echo' && context . args ) {
await context . send ( context . args . join ( ' ' ));
}
});
Error Handling Middleware
Global Error Handler
vk . updates . use ( async ( context , next ) => {
try {
await next ();
} catch ( error ) {
console . error ( 'Error in middleware chain:' , error );
if ( context . is ( 'message' )) {
await context . send ( 'An error occurred. Please try again later.' );
}
}
});
Specific Error Types
Real example from VK-IO examples:
const { VK , APIError } = require ( 'vk-io' );
const { HearManager } = require ( '@vk-io/hear' );
const vk = new VK ({ token: process . env . TOKEN });
const hearManager = new HearManager ();
// Custom catch all errors
vk . updates . use ( async ( context , next ) => {
try {
await next ();
} catch ( error ) {
console . error ( 'An error has occurred' , error );
}
});
class MyNetworkError extends Error {}
// Custom error handling
vk . updates . use ( async ( context , next ) => {
try {
await next ();
} catch ( error ) {
// Only handle message errors
if ( ! context . is ( 'message' )) {
throw error ;
}
// Handle specific API error (no chat access)
if ( error instanceof APIError && error . code === 917 ) {
await context . send ( 'I do not have access to the chat, please give it to me.' );
return ;
}
// Handle custom network error
if ( error instanceof MyNetworkError ) {
await context . send ( 'There was a problem with the connection.' );
return ;
}
// Re-throw unknown errors
throw error ;
}
});
vk . updates . on ( 'message_new' , hearManager . middleware );
hearManager . hear ( /get chat/ i , async context => {
if ( ! context . isChat ) {
return context . send ( 'We are not in chat!' );
}
// This may throw error 917 if no access
const { items } = await vk . api . messages . getConversationsById ({
peer_ids: context . peerId ,
});
const [ conversation ] = items ;
return context . send ( `Chat: ${ JSON . stringify ( conversation , null , ' ' ) } ` );
});
hearManager . hear ( /throw network error/ i , async () => {
throw new MyNetworkError ();
});
Error Recovery
import { APIError , APIErrorCode } from 'vk-io' ;
vk . updates . use ( async ( context , next ) => {
try {
await next ();
} catch ( error ) {
if ( error instanceof APIError ) {
switch ( error . code ) {
case APIErrorCode . TOO_MANY_REQUESTS :
console . log ( 'Rate limit hit, waiting...' );
await new Promise ( resolve => setTimeout ( resolve , 1000 ));
await next (); // Retry
break ;
case APIErrorCode . FLOOD_CONTROL :
console . log ( 'Flood control triggered' );
if ( context . is ( 'message' )) {
await context . send ( 'Please wait before sending more messages.' );
}
break ;
default :
throw error ; // Re-throw other API errors
}
} else {
throw error ; // Re-throw non-API errors
}
}
});
Conditional Middleware
Type-Based Middleware
// Only for messages
vk . updates . on ( 'message_new' , async ( context , next ) => {
console . log ( 'Message received:' , context . text );
await next ();
});
// Only for wall posts
vk . updates . on ( 'wall_post_new' , async ( context , next ) => {
console . log ( 'New post:' , context . text );
await next ();
});
// Multiple event types
vk . updates . on ([ 'message_new' , 'message_edit' ], async ( context , next ) => {
console . log ( 'Message event:' , context . type );
await next ();
});
Custom Conditions
function onlyChats ( middleware ) {
return async ( context , next ) => {
if ( context . is ( 'message' ) && context . isChat ) {
return middleware ( context , next );
}
await next ();
};
}
function onlyAdmins ( middleware ) {
return async ( context , next ) => {
if ( context . isAdmin ) {
return middleware ( context , next );
}
await next ();
};
}
// Usage
vk . updates . use (
onlyChats (
onlyAdmins (
async ( context ) => {
await context . send ( 'Admin chat command executed' );
}
)
)
);
Async Middleware
Database Operations
interface UserContext extends MessageContext {
dbUser ?: any ;
}
vk . updates . use ( async ( context : UserContext , next ) => {
if ( ! context . is ( 'message' )) {
return next ();
}
// Fetch user from database
context . dbUser = await database . users . findById ( context . senderId );
if ( ! context . dbUser ) {
// Create new user
context . dbUser = await database . users . create ({
id: context . senderId ,
firstSeen: new Date (),
});
}
await next ();
// Update user after processing
await database . users . update ( context . senderId , {
lastSeen: new Date (),
});
});
External API Calls
vk . updates . use ( async ( context , next ) => {
if ( ! context . is ( 'message' )) {
return next ();
}
// Enrich context with external data
const userInfo = await fetch (
`https://api.example.com/users/ ${ context . senderId } `
). then ( r => r . json ());
context . externalUserData = userInfo ;
await next ();
});
Middleware Composition
Creating Middleware Factory
function createLogger ( prefix : string ) {
return async ( context , next ) => {
console . log ( `[ ${ prefix } ] Event:` , context . type );
await next ();
};
}
vk . updates . use ( createLogger ( 'BOT' ));
Combining Middleware
import { compose } from 'middleware-io' ;
const authMiddleware = async ( context , next ) => {
context . isAuthenticated = true ;
await next ();
};
const loggingMiddleware = async ( context , next ) => {
console . log ( 'Processing:' , context . type );
await next ();
};
const rateLimitMiddleware = async ( context , next ) => {
// Rate limiting logic
await next ();
};
// Combine multiple middleware
const combined = compose ([
authMiddleware ,
loggingMiddleware ,
rateLimitMiddleware ,
]);
vk . updates . use ( combined );
Using External Middleware
@vk-io/hear Integration
import { HearManager } from '@vk-io/hear' ;
const hearManager = new HearManager ();
// Use hear manager as middleware
vk . updates . on ( 'message_new' , hearManager . middleware );
// Define patterns
hearManager . hear ( /start/ i , async ( context ) => {
await context . send ( 'Bot started!' );
});
hearManager . hear ( /hello/ i , async ( context ) => {
await context . send ( 'Hello!' );
});
@vk-io/session Integration
import { SessionManager } from '@vk-io/session' ;
const sessionManager = new SessionManager ();
// Use session manager as middleware
vk . updates . on ( 'message_new' , sessionManager . middleware );
vk . updates . hear ( /counter/ i , async ( context ) => {
const { session } = context ;
if ( ! session . counter ) {
session . counter = 0 ;
}
session . counter += 1 ;
await context . send ( `Counter: ${ session . counter } ` );
});
Best Practices
Order Matters : Middleware executes in the order it’s registered. Place error handlers early.
Always Call Next : Unless you intentionally want to stop the chain, always call await next().
Handle Errors : Wrap middleware chains in try-catch blocks to prevent crashes.
Type Safety : Extend context types when adding custom properties.
Keep It Simple : Each middleware should have a single, clear purpose.
Middleware Ordering
// 1. Error handler (first)
vk . updates . use ( errorHandler );
// 2. Logging
vk . updates . use ( logger );
// 3. Authentication
vk . updates . use ( auth );
// 4. Rate limiting
vk . updates . use ( rateLimit );
// 5. Business logic (last)
vk . updates . use ( handlers );
Type-Safe Middleware
import { Middleware } from 'middleware-io' ;
import { MessageContext } from 'vk-io' ;
interface CustomContext extends MessageContext {
user : {
id : number ;
name : string ;
role : string ;
};
}
const middleware : Middleware < CustomContext > = async ( context , next ) => {
// TypeScript knows about context.user
console . log ( context . user . name );
await next ();
};
vk . updates . use ( middleware );
Be careful with infinite loops in middleware. Always ensure the chain can complete.
Middleware system is implemented in:
/packages/vk-io/src/updates/updates.ts:347 - use() method for adding middleware
VK-IO uses the middleware-io package for the underlying middleware composition
Examples: /docs/examples/advanced/middleware-error-fallback.js and /docs/examples/advanced/context-modification.js