Documentation Index Fetch the complete documentation index at: https://mintlify.com/meteor/meteor/llms.txt
Use this file to discover all available pages before exploring further.
The webapp package provides Meteor’s built-in HTTP server, serving your application over HTTP and managing client-server communication.
Installation
The webapp package is included by default in all Meteor applications.
Overview
WebApp provides:
HTTP Server - Built on Express 5.x
Static Asset Serving - Automatic bundling and serving of client files
Middleware Support - Express middleware integration
WebSocket Support - For DDP connections via SockJS
Hot Code Push - Automatic client updates
Modern/Legacy Bundles - Differential serving based on browser capabilities
Version : 2.1.0
Summary : Serves a Meteor app over HTTP
Dependencies : express@5.1.0, compression@1.7.4, cookie-parser@1.4.6
Exports : WebApp (server), WebAppInternals (server)
Basic Usage
Server-Side Exports
import { WebApp } from 'meteor/webapp' ;
import { WebAppInternals } from 'meteor/webapp' ;
Client-Side
import { WebApp } from 'meteor/webapp' ;
// Client-side API is minimal
console . log ( 'App version:' , WebApp . clientHash );
Express Integration
WebApp is built on Express 5.x. You can add middleware to the Connect/Express server:
Adding Middleware
import { WebApp } from 'meteor/webapp' ;
// Add middleware to the Express app
WebApp . connectHandlers . use (( req , res , next ) => {
console . log ( 'Request:' , req . method , req . url );
next ();
});
// Middleware for specific routes
WebApp . connectHandlers . use ( '/api' , ( req , res , next ) => {
res . setHeader ( 'X-Custom-Header' , 'value' );
next ();
});
Custom Routes
// Serve custom content
WebApp . connectHandlers . use ( '/health' , ( req , res ) => {
res . writeHead ( 200 , { 'Content-Type' : 'application/json' });
res . end ( JSON . stringify ({ status: 'ok' , time: new Date () }));
});
// API endpoint
WebApp . connectHandlers . use ( '/api/data' , async ( req , res ) => {
const data = await fetchData ();
res . writeHead ( 200 , { 'Content-Type' : 'application/json' });
res . end ( JSON . stringify ( data ));
});
Using Express Middleware
import bodyParser from 'body-parser' ;
import cors from 'cors' ;
// Parse JSON bodies
WebApp . connectHandlers . use ( bodyParser . json ());
// Enable CORS
WebApp . connectHandlers . use ( cors ({
origin: 'https://trusted-site.com' ,
credentials: true
}));
// Custom middleware
WebApp . connectHandlers . use (( req , res , next ) => {
// Add request ID
req . id = Math . random (). toString ( 36 ). substr ( 2 , 9 );
next ();
});
Raw Connect Handlers
// Access the raw Connect/Express app
const app = WebApp . connectHandlers ;
// Use Express routing
app . get ( '/users/:id' , ( req , res ) => {
const userId = req . params . id ;
res . json ({ userId , name: 'Example' });
});
app . post ( '/submit' , ( req , res ) => {
console . log ( 'Form submitted:' , req . body );
res . json ({ success: true });
});
HTTP Handlers
Request Handler
WebApp . connectHandlers . use (( req , res , next ) => {
// req: Node.js IncomingMessage
// res: Node.js ServerResponse
console . log ( 'Method:' , req . method );
console . log ( 'URL:' , req . url );
console . log ( 'Headers:' , req . headers );
console . log ( 'IP:' , req . connection . remoteAddress );
next ();
});
Response Manipulation
WebApp . connectHandlers . use (( req , res , next ) => {
// Add custom headers
res . setHeader ( 'X-Powered-By' , 'Meteor' );
res . setHeader ( 'X-Frame-Options' , 'DENY' );
// Override response
if ( req . url === '/custom' ) {
res . writeHead ( 200 , { 'Content-Type' : 'text/html' });
res . end ( '<h1>Custom Page</h1>' );
return ;
}
next ();
});
Static File Serving
Custom Static Files
import path from 'path' ;
import express from 'express' ;
// Serve additional static directory
const staticPath = path . join ( process . cwd (), 'public' );
WebApp . connectHandlers . use ( '/public' , express . static ( staticPath ));
Download Handler
WebApp . connectHandlers . use ( '/download/:file' , ( req , res ) => {
const filename = req . params . file ;
const filepath = path . join ( process . cwd (), 'files' , filename );
res . download ( filepath , ( err ) => {
if ( err ) {
res . status ( 404 ). send ( 'File not found' );
}
});
});
Client Serving
Modify HTML Before Serving
import { WebAppInternals } from 'meteor/webapp' ;
// Inject content into HTML
WebApp . addHtmlAttributeHook (( request ) => {
// Add attributes to <html> tag
return {
lang: 'en' ,
'data-theme' : 'light'
};
});
Runtime Config
// Set client runtime configuration
WebApp . addRuntimeConfigHook (( options ) => {
// Add data available to client as __meteor_runtime_config__
options . runtimeConfig . customSetting = 'value' ;
});
Suppressing Default Behavior
Suppress Connect Handlers
// Prevent Meteor from serving specific paths
WebApp . suppressConnectErrors ();
WebApp . connectHandlers . use ( '/external' , ( req , res , next ) => {
// Handle without Meteor's error handling
res . end ( 'Custom handler' );
});
Server Listening
Get Server Instance
import { WebApp } from 'meteor/webapp' ;
// Access underlying HTTP server
const httpServer = WebApp . httpServer ;
// Listen for server events
httpServer . on ( 'listening' , () => {
console . log ( 'Server is listening' );
});
httpServer . on ( 'connection' , ( socket ) => {
console . log ( 'New connection from' , socket . remoteAddress );
});
Custom Port and Host
# Set via environment variables
export PORT = 4000
export BIND_IP = 127.0.0.1
meteor
# Or use --port flag
meteor --port 4000
Compression
WebApp includes automatic response compression:
// Compression is enabled by default
// Customize if needed by adding compression middleware
import compression from 'compression' ;
WebApp . connectHandlers . use ( compression ({
level: 6 , // Compression level (0-9)
threshold: 1024 // Only compress if > 1KB
}));
// Add security headers
WebApp . connectHandlers . use (( req , res , next ) => {
// Prevent clickjacking
res . setHeader ( 'X-Frame-Options' , 'SAMEORIGIN' );
// XSS protection
res . setHeader ( 'X-Content-Type-Options' , 'nosniff' );
res . setHeader ( 'X-XSS-Protection' , '1; mode=block' );
// HTTPS only (if using SSL)
res . setHeader ( 'Strict-Transport-Security' ,
'max-age=31536000; includeSubDomains' );
// Content Security Policy
res . setHeader ( 'Content-Security-Policy' ,
"default-src 'self'; script-src 'self' 'unsafe-inline'" );
next ();
});
Cookie Parsing
WebApp includes cookie-parser:
WebApp . connectHandlers . use (( req , res , next ) => {
// Access parsed cookies
console . log ( 'Cookies:' , req . cookies );
console . log ( 'Signed cookies:' , req . signedCookies );
// Set cookie
res . cookie ( 'sessionId' , '123' , {
httpOnly: true ,
secure: true ,
maxAge: 86400000 // 24 hours
});
next ();
});
Request Logging
// Simple request logger
WebApp . connectHandlers . use (( req , res , next ) => {
const start = Date . now ();
// Log when response finishes
res . on ( 'finish' , () => {
const duration = Date . now () - start ;
console . log (
` ${ req . method } ${ req . url } ${ res . statusCode } ${ duration } ms`
);
});
next ();
});
Error Handling
// Custom error handler (must be last)
WebApp . connectHandlers . use (( err , req , res , next ) => {
console . error ( 'Server error:' , err );
res . status ( 500 ). json ({
error: 'Internal Server Error' ,
message: err . message ,
stack: process . env . NODE_ENV === 'development' ? err . stack : undefined
});
});
Client Hash
WebApp provides version information for cache busting:
// Server
import { WebApp } from 'meteor/webapp' ;
console . log ( 'Client hash:' , WebApp . clientHash );
Meteor . methods ({
'getClientVersion' () {
return WebApp . clientHash ;
}
});
// Client
import { WebApp } from 'meteor/webapp' ;
console . log ( 'My version:' , WebApp . clientHash );
// Check if new version available
Meteor . call ( 'getClientVersion' , ( err , serverVersion ) => {
if ( serverVersion !== WebApp . clientHash ) {
console . log ( 'New version available!' );
}
});
Hot Code Push
Detect Reload
// Client: Detect when reload is triggered
Meteor . startup (() => {
if ( WebApp . reloadImmediately ) {
console . log ( 'App will reload soon' );
}
});
Customize Reload Behavior
// Client: Control when reload happens
Reload . _onMigrate (( retry ) => {
// Ask user if they want to reload
if ( confirm ( 'New version available. Reload now?' )) {
return [ true ]; // Allow reload
}
// Retry later
setTimeout (() => retry (), 60000 );
return [ false ]; // Prevent reload for now
});
Modern vs Legacy Bundles
WebApp serves different JavaScript bundles based on browser capabilities:
// Automatic differential serving
// - Modern browsers get smaller, faster ES6+ code
// - Legacy browsers get transpiled ES5 code
// No configuration needed - happens automatically!
WebSocket Configuration
// Configure SockJS (used by DDP)
process . env . DISABLE_WEBSOCKETS = 'false' ;
// In code:
WebApp . httpServer . on ( 'upgrade' , ( req , socket , head ) => {
console . log ( 'WebSocket upgrade request' );
});
Complete Example: REST API
import { WebApp } from 'meteor/webapp' ;
import bodyParser from 'body-parser' ;
// Parse JSON bodies
WebApp . connectHandlers . use ( bodyParser . json ());
// Authentication middleware
const authenticate = ( req , res , next ) => {
const token = req . headers . authorization ?. replace ( 'Bearer ' , '' );
if ( ! token ) {
return res . status ( 401 ). json ({ error: 'No token provided' });
}
// Validate token
const user = validateToken ( token );
if ( ! user ) {
return res . status ( 401 ). json ({ error: 'Invalid token' });
}
req . user = user ;
next ();
};
// Public endpoint
WebApp . connectHandlers . get ( '/api/status' , ( req , res ) => {
res . json ({ status: 'ok' , timestamp: new Date () });
});
// Protected endpoints
WebApp . connectHandlers . get ( '/api/user' , authenticate , ( req , res ) => {
res . json ({ user: req . user });
});
WebApp . connectHandlers . post ( '/api/data' , authenticate , async ( req , res ) => {
try {
const result = await processData ( req . body , req . user );
res . json ({ success: true , result });
} catch ( error ) {
res . status ( 400 ). json ({ error: error . message });
}
});
// Error handler
WebApp . connectHandlers . use (( err , req , res , next ) => {
console . error ( 'API Error:' , err );
res . status ( 500 ). json ({ error: 'Internal Server Error' });
});
Enable compression - WebApp includes compression middleware by default
Cache static assets - Set appropriate cache headers for static files
Use CDN - Serve static assets from a CDN in production
// Set cache headers for static assets
WebApp . connectHandlers . use ( '/assets' , ( req , res , next ) => {
res . setHeader ( 'Cache-Control' , 'public, max-age=31536000' );
next ();
});
ddp DDP protocol over WebSocket
autoupdate Hot code push system
boilerplate-generator HTML generation for client
browser-policy Security headers and CSP
Source Code