Complete documentation for Expressify's API. Easily build web applications with our intuitive and powerful interface.
This documentation covers all the methods, properties, and features available in Expressify.
Creates an Expressify application.
from expressify import expressify
app = expressify()
Starts a server and listens for connections on the specified host and port.
Required port number
Required hostname (e.g., '127.0.0.1')
Optional function called when server starts
app.listen(port=5000, hostname='0.0.0.0', callback=lambda: print('Server running on port 5000'))
Mounts middleware at the specified path or globally.
Path string or middleware function
Middleware function if path is provided
# Global middleware
app.use(logger_middleware)
# Path-specific middleware
app.use('/admin', auth_middleware)
Assigns a setting to the application.
Setting name
Setting value
app.set('view engine', 'jinja2')
app.set('views', './views')
Object for app-level data that persists throughout the lifetime of the application.
app.locals.title = 'My Application'
app.locals.version = '1.0.0'
Setting | Type | Description |
---|---|---|
view engine | String | The template engine to use ('jinja2') |
views | String | Directory path for template files |
trust proxy | Boolean | When true, trust the X-Forwarded-* headers |
json spaces | Number | Spaces for JSON responses indentation |
Expressify provides methods for handling different types of HTTP requests. Each method represents a standard HTTP verb and allows you to define how your application responds to client requests.
Handles GET requests to retrieve data from the server. Used when clients want to read or fetch resources.
Handles POST requests to create new resources on the server. Used when clients want to submit data.
Handles PUT requests to completely replace existing resources. Used for updating entire resources.
Handles DELETE requests to remove resources from the server. Used when clients want to delete data.
Handles PATCH requests for partial updates to resources. Used when clients want to modify only specific parts of data.
Handles all HTTP methods at a specific path. Useful for setting up middleware that should run for all request types.
The URL pattern that the route should match. Can include route parameters like :id
to capture values from the URL.
The function that processes the request. Must accept req
and res
parameters.
List of middleware functions to run before the handler. Each middleware must accept req
, res
, and next
parameters.
Decorator syntax is more Pythonic and similar to other Python web frameworks like Flask.
from expressify import expressify
app = expressify()
# Basic GET route
@app.get('/users')
def get_users(req, res):
res.json([{'name': 'John'}, {'name': 'Jane'}])
# POST route with JSON response
@app.post('/users')
def create_user(req, res):
user_data = req.body
# Process user data...
res.status(201).json({'message': 'User created', 'user': user_data})
# Route with path parameter
@app.get('/users/:id')
def get_user(req, res):
user_id = req.params.get('id')
# Fetch user from database...
res.json({'id': user_id, 'name': 'Example User'})
# Route with middleware
@app.get('/admin/dashboard', middleware=[auth_middleware])
def admin_dashboard(req, res):
res.send('Admin Dashboard')
Route parameters let you capture values from specific parts of the URL path. They act as placeholders in your route definitions, making your routes more flexible and powerful.
:
prefix in the route pathreq.params.get('parameter_name')
@app.get('/users/:id')
def get_user(req, res):
user_id = req.params.get('id')
res.send(f'User ID: {user_id}')
Visiting /users/42
would display "User ID: 42".
def get_user(req, res):
user_id = req.params.get('id')
res.send(f'User ID: {user_id}')
app.get('/users/:id', get_user)
Express-style approach for the same route.
@app.get('/users/:user_id/posts/:post_id')
def get_user_post(req, res):
user_id = req.params.get('user_id')
post_id = req.params.get('post_id')
res.send(f'User {user_id}, Post {post_id}')
Visiting /users/42/posts/123
would display "User 42, Post 123".
def get_user_post(req, res):
user_id = req.params.get('user_id')
post_id = req.params.get('post_id')
res.send(f'User {user_id}, Post {post_id}')
app.get('/users/:user_id/posts/:post_id', get_user_post)
Express-style approach for multiple parameters.
Route handlers are functions that run when a matching request is received. They define what happens when someone visits a specific URL in your application.
Every route handler must accept two parameters:
Contains information about the HTTP request, including query parameters, route parameters, headers, and body.
Provides methods to send a response to the client, such as send()
, json()
, and status()
.
@app.get('/hello')
def hello(req, res):
res.send('Hello, World!
')
def hello(req, res):
res.send('Hello, World!
')
app.get('/hello', hello)
@app.get('/api/data')
def get_data(req, res):
res.json({
'name': 'Example',
'items': [1, 2, 3]
})
def get_data(req, res):
res.json({
'name': 'Example',
'items': [1, 2, 3]
})
app.get('/api/data', get_data)
@app.post('/api/items')
def create_item(req, res):
# Create item logic...
res.status(201).json({'message': 'Created'})
def create_item(req, res):
# Create item logic...
res.status(201).json({'message': 'Created'})
app.post('/api/items', create_item)
@app.get('/api/protected')
def protected(req, res):
if not is_authenticated(req):
return res.status(401).json({
'error': 'Unauthorized'
})
def protected(req, res):
if not is_authenticated(req):
return res.status(401).json({
'error': 'Unauthorized'
})
app.get('/api/protected', protected)
Response methods allow you to send different types of data back to the client. Each method terminates the request-response cycle unless otherwise specified.
Sends a response with the provided data. The data type determines the Content-Type header.
@app.get('/hello')
def hello(req, res):
res.send('Hello, World!')
Sends a JSON response. Automatically sets the Content-Type header to application/json.
@app.get('/api/data')
def get_data(req, res):
res.json({
'name': 'Example',
'items': [1, 2, 3],
'active': True
})
Sets the HTTP status code for the response. This method is chainable.
@app.post('/api/items')
def create_item(req, res):
# Create item logic...
res.status(201).json({
'message': 'Item created',
'id': 'new-item-id'
})
Redirects the request to the specified path with status code 302 by default.
@app.get('/old-page')
def old_page(req, res):
# Temporary redirect (302)
res.redirect('/new-page')
Many response methods return the response object, allowing you to chain multiple methods together for more concise code.
@app.get('/api/chainable')
def chainable(req, res):
res.status(200)
.set_header('X-Custom-Header', 'value')
.set_header('Content-Type', 'application/json')
.send('{"message": "Success"}')
Only methods that don't end the request-response cycle (like status()
and set_header()
) can be chained.
Middleware functions are functions that have access to the request object, response object, and the next middleware function in the application's request-response cycle. Middleware can:
Every middleware function follows the same pattern and must accept three parameters:
Contains information about the HTTP request.
Provides methods to send responses.
A function that passes control to the next middleware. You must call next()
unless you want to end the request cycle.
Expressify provides two different ways to register middleware in your application. You can use either the decorator syntax (Python-idiomatic) or the Express-like function call syntax. Both approaches achieve the same result.
The decorator syntax is more Pythonic and allows you to apply middleware with the @app.use
decorator or directly in route definitions.
from expressify import expressify
app = expressify()
# Global middleware using decorator
@app.use
def logger_middleware(req, res, next):
print(f"{req.method} {req.path}")
next()
# Define authentication middleware
def auth_middleware(req, res, next):
if not req.headers.get('Authorization'):
return res.status(401).send('Unauthorized')
next()
# Regular route - no middleware
@app.get('/')
def home(req, res):
res.send('Home page')
# Route with middleware parameter
@app.get('/admin', [auth_middleware])
def admin(req, res):
res.send('Admin dashboard')
# Multiple middleware functions
@app.get('/profile', [auth_middleware, logger_middleware])
def profile(req, res):
res.send('User profile')
if __name__ == '__main__':
app.listen(port=3000, hostname='127.0.0.1')
def auth_middleware(req, res, next):
"""Check for API key on protected routes"""
if req.path.startswith('/protected'):
api_key = req.get_header('x-api-key')
if not api_key:
return res.status(401).json({
'error': 'Authentication required',
'message': 'Missing API key. Please provide X-API-Key header.'
})
if api_key != 'abc123':
return res.status(403).json({
'error': 'Access denied',
'message': 'Invalid API key'
})
# If authentication passes, add user info to request
req.user = {'id': 123, 'role': 'user'}
# Call the next middleware/route handler
return next()
# Apply middleware to all routes
app.use(auth_middleware)
# This route requires API key
@app.get('/protected/data')
def protected_data(req, res):
# The auth middleware will have already verified the API key
return res.json({
'message': 'You have accessed protected data',
'user': req.user,
'timestamp': time.time()
})
# Rate Limiter Middleware
def rate_limiter(max_requests=100, window_seconds=60):
"""
Creates a rate limiting middleware function
Args:
max_requests (int): Maximum number of requests allowed
window_seconds (int): Time window in seconds
"""
# Store request timestamps for each client
clients = {}
def middleware(req, res, next):
client_ip = req.get_header("x-forwarded-for") or req.remote_addr
# Initialize client record if doesn't exist
if client_ip not in clients:
clients[client_ip] = []
# Get current time
now = time.time()
# Remove requests outside the window
clients[client_ip] = [t for t in clients[client_ip] if now - t < window_seconds]
# Check if client has exceeded rate limit
if len(clients[client_ip]) >= max_requests:
return res.status(429).json({
"error": "Too Many Requests",
"message": f"Rate limit of {max_requests} requests per {window_seconds} seconds exceeded"
})
# Add current request timestamp
clients[client_ip].append(now)
# Add rate limit headers
remaining = max_requests - len(clients[client_ip])
res.set_header("X-RateLimit-Limit", str(max_requests))
res.set_header("X-RateLimit-Remaining", str(remaining))
res.set_header("X-RateLimit-Reset", str(int(now + window_seconds)))
return next()
return middleware
# Create API rate limiter with custom limits
api_rate_limiter = rate_limiter(max_requests=5, window_seconds=10)
# Apply to specific routes
@app.get('/api/status', [api_rate_limiter])
def api_status(req, res):
return res.json({
"status": "online",
"timestamp": time.time()
})
For error handling middleware, it's recommended to define it as the first middleware in your application. This ensures that any errors thrown in subsequent middleware or route handlers will be caught and handled properly.
Middleware functions are executed in the order they are defined. This means that the order in which you register middleware is important.
from expressify import expressify
app = expressify()
# Register middleware in specific order
@app.use
def middleware1(req, res, next):
print("Middleware 1 - Before")
next()
print("Middleware 1 - After")
@app.use
def middleware2(req, res, next):
print("Middleware 2 - Before")
next()
print("Middleware 2 - After")
@app.get('/')
def home(req, res):
print("Route Handler")
res.send('Home')
# Output for a request to '/' would be:
# Middleware 1 - Before
# Middleware 2 - Before
# Route Handler
# Middleware 2 - After
# Middleware 1 - After
The request object contains information about the incoming request, such as the HTTP method, URL, headers, and body.
Headers are key-value pairs that provide additional information about the request.
Query parameters are appended to the URL and are used to send data to the server.
@app.get('/search')
def search(req, res):
query = req.query.get('q')
res.send(f'Searching for: {query}')
Route parameters are part of the URL path and are used to capture values from the URL.
@app.get('/users/:id')
def get_user(req, res):
user_id = req.params.get('id')
res.send(f'User ID: {user_id}')
The request body contains data sent by the client to the server.
@app.post('/users')
def create_user(req, res):
user_data = req.body
# Process user data...
res.status(201).json({'message': 'User created', 'user': user_data})
The request object provides several methods to access information about the request.
Returns the HTTP method of the request.
@app.get('/')
def home(req, res):
res.send(f"Method: {req.method}")
Returns the full URL of the request.
@app.get('/')
def home(req, res):
res.send(f"URL: {req.url}")
Returns a dictionary of request headers.
@app.get('/')
def home(req, res):
res.send(f"Headers: {req.headers}")
Returns a dictionary of query parameters.
@app.get('/search')
def search(req, res):
query = req.query.get('q')
res.send(f'Searching for: {query}')
Returns a dictionary of route parameters.
@app.get('/users/:id')
def get_user(req, res):
user_id = req.params.get('id')
res.send(f'User ID: {user_id}')
Contains the parsed request body.
@app.post('/users')
def create_user(req, res):
user_data = req.body
# Process user data...
res.status(201).json({'message': 'User created', 'user': user_data})
The response object is used to send data back to the client. It provides methods to set the status code, headers, and body.
Use res.status(code)
to set the HTTP status code.
@app.get('/')
def home(req, res):
res.status(200).send('OK')
Use res.set_header('Header-Name', 'Value')
to set custom headers.
@app.get('/')
def home(req, res):
res.set_header('X-Custom-Header', 'Value')
res.send('OK')
Use res.send(data)
to send various types of data back to the client.
@app.get('/')
def home(req, res):
res.send('Hello, World!')
Use res.json(obj)
to send JSON data back to the client.
@app.get('/api/data')
def get_data(req, res):
res.json({
'name': 'Example',
'items': [1, 2, 3]
})
Use res.redirect(path)
to redirect the request to a different path.
@app.get('/old-page')
def old_page(req, res):
# Temporary redirect (302)
res.redirect('/new-page')
The response object provides several methods to send data back to the client.
Sets the HTTP status code for the response.
@app.get('/')
def home(req, res):
res.status(200).send('OK')
Sets a custom header for the response.
@app.get('/')
def home(req, res):
res.set_header('X-Custom-Header', 'Value')
res.send('OK')
Sets the Content-Type HTTP header for the response.
# Setting different content types
@app.get('/text')
def text(req, res):
res.type('text/plain').send('Plain text content')
@app.get('/html')
def html(req, res):
res.type('text/html').send('HTML Content
')
@app.get('/xml')
def xml(req, res):
res.type('application/xml').send('XML Content ')
@app.get('/css')
def css(req, res):
res.type('text/css').send('body { color: blue; }')
Sends various types of data back to the client.
@app.get('/')
def home(req, res):
res.send('Hello, World!')
Sends JSON data back to the client.
@app.get('/api/data')
def get_data(req, res):
res.json({
'name': 'Example',
'items': [1, 2, 3]
})
Redirects the request to a different path.
@app.get('/old-page')
def old_page(req, res):
# Temporary redirect (302)
res.redirect('/new-page')
Middleware functions are functions that have access to the request object, response object, and the next middleware function in the application's request-response cycle. Middleware can:
Every middleware function follows the same pattern and must accept three parameters:
Contains information about the HTTP request.
Provides methods to send responses.
A function that passes control to the next middleware. You must call next()
unless you want to end the request cycle.
There are several ways to register middleware in your application. You can apply middleware globally to all routes, to specific routes, or to routes under a specific path prefix.
Decorator syntax provides a cleaner way to apply middleware with the @app.use
decorator or through route parameters.
from expressify import expressify
app = expressify()
# Global middleware using decorator
@app.use
def logger_middleware(req, res, next):
print(f"{req.method} {req.path}")
next()
# Authentication middleware for protected routes
def auth_middleware(req, res, next):
if not req.headers.get('Authorization'):
return res.status(401).send('Unauthorized')
next()
# Regular route - no middleware
@app.get('/')
def home(req, res):
res.send('Home page')
# Route with middleware param
@app.get('/admin', middleware=[auth_middleware])
def admin(req, res):
res.send('Admin dashboard')
# Multiple middleware functions
@app.get('/profile', middleware=[auth_middleware, logger_middleware])
def profile(req, res):
res.send('User profile')
if __name__ == '__main__':
app.listen(port=3000, hostname="127.0.0.1")
def auth_middleware(req, res, next):
"""Check for API key on protected routes"""
if req.path.startswith('/protected'):
api_key = req.get_header('x-api-key')
if not api_key:
return res.status(401).json({
'error': 'Authentication required',
'message': 'Missing API key. Please provide X-API-Key header.'
})
if api_key != 'abc123':
return res.status(403).json({
'error': 'Access denied',
'message': 'Invalid API key'
})
# If authentication passes, add user info to request
req.user = {'id': 123, 'role': 'user'}
# Call the next middleware/route handler
return next()
# Apply middleware to all routes
app.use(auth_middleware)
# This route requires API key
@app.get('/protected/data')
def protected_data(req, res):
# The auth middleware will have already verified the API key
return res.json({
'message': 'You have accessed protected data',
'user': req.user,
'timestamp': time.time()
})
import time
def logger_middleware(req, res, next):
# Start time
start_time = time.time()
# Log request
print(f"[{time.strftime('%Y-%m-%d %H:%M:%S')}] {req.method} {req.path}")
# Store start time on request object
req.start_time = start_time
# Call next middleware
next()
# Calculate duration after response
duration = time.time() - start_time
# Log response
print(f"Response: {res.status_code} in {duration:.2f}s")
def cors_middleware(req, res, next):
# Allow requests from any origin
res.set_header('Access-Control-Allow-Origin', '*')
# Allow specific HTTP methods
res.set_header('Access-Control-Allow-Methods',
'GET, POST, PUT, DELETE, OPTIONS')
# Allow specific headers
res.set_header('Access-Control-Allow-Headers',
'Content-Type, Authorization')
# Handle preflight requests
if req.method == 'OPTIONS':
return res.status(204).send()
next()
def error_handler(req, res, next):
try:
next()
except Exception as e:
print(f"Error: {str(e)}")
# Send appropriate response based on error
status_code = getattr(e, 'status_code', 500)
message = getattr(e, 'message', str(e))
res.status(status_code).json({
'error': True,
'message': message
})
For error handling middleware, it's recommended to define it as the first middleware in your application. This ensures that any errors thrown in subsequent middleware or route handlers will be caught and handled properly.
Middleware functions are executed in the order they are defined. This means that the order in which you register middleware is important.
from expressify import expressify
app = expressify()
# Register middleware in specific order
@app.use
def middleware1(req, res, next):
print("Middleware 1 - Before")
next()
print("Middleware 1 - After")
@app.use
def middleware2(req, res, next):
print("Middleware 2 - Before")
next()
print("Middleware 2 - After")
@app.get('/')
def home(req, res):
print("Route Handler")
res.send('Home')
# Output for a request to '/' would be:
# Middleware 1 - Before
# Middleware 2 - Before
# Route Handler
# Middleware 2 - After
# Middleware 1 - After
Expressify uses Jinja2 as its template engine. Jinja2 is a powerful templating engine for Python.
Templates are stored in a directory called views
. You can set this directory using the views
setting in your application.
from expressify import expressify
import os
app = expressify()
# Set the template engine to Jinja2
app.set('view engine', 'jinja2')
# Set the templates directory
app.set('views', os.path.join(os.path.dirname(__file__), 'views'))
Use the res.render()
method to render templates.
@app.get('/')
def home(req, res):
res.render('index.html')
You can pass variables to templates using the res.render()
method.
# Method 1: Pass data as a dictionary
@app.get('/example1')
def example1(req, res):
res.render('index.html', {
'title': 'Home',
'message': 'Welcome to Expressify',
'items': ['Item 1', 'Item 2', 'Item 3']
})
# Method 2: Express.js style - store data in a variable first
@app.get('/example2')
def example2(req, res):
# Data to pass to the view
data = {
'title': 'Hello, World!',
'message': 'Welcome to my Expressify app!',
'user': {
'name': 'John Doe',
'age': 30
}
}
# Render the view and pass the data
res.render('index.html', data)
Jinja2 supports template inheritance, allowing you to create a base template that other templates can extend.
{% extends "base.html" %}
{% block content %}
Welcome to Expressify
{% endblock %}
Jinja2 provides several built-in filters that you can use in your templates.
You can also define your own filters in Python.
from jinja2 import Environment, FileSystemLoader
env = Environment(loader=FileSystemLoader('templates'))
def format_date(date):
return date.strftime('%Y-%m-%d')
env.filters['format_date'] = format_date
Expressify provides a built-in middleware for serving static files such as CSS, JavaScript, and images.
There are two main approaches to serving static files:
from expressify import expressify
from expressify.lib.middleware import Middleware
import os
app = expressify()
# Method 1: Serve files from the 'public' directory at the root URL path
public_dir = os.path.join(os.path.dirname(__file__), 'public')
app.use(Middleware.static(public_dir))
# Method 2: Serve files from a directory at a specific URL path
# This mounts the 'public/assets' directory at the '/assets' URL path
assets_dir = os.path.join(os.path.dirname(__file__), 'public', 'assets')
app.use('/assets', Middleware.static(assets_dir))
You can also serve specific files directly.
from expressify.lib.middleware import Middleware
import os
favicon_path = os.path.join(os.path.dirname(__file__), 'public', 'favicon.ico')
app.get('/favicon.ico', lambda req, res: res.sendFile(favicon_path))
You can use static files in your templates by directly referencing their paths:
Expressify provides a simple way to handle errors using middleware.
You can define error handling middleware to catch exceptions and send a custom response.
def error_handler(req, res, next):
try:
next()
except Exception as e:
print(f"Error: {str(e)}")
# Send appropriate response based on error
status_code = getattr(e, 'status_code', 500)
message = getattr(e, 'message', str(e))
res.status(status_code).json({
'error': True,
'message': message
})
You can also define custom error pages for specific status codes.
from expressify import expressify
app = expressify()
# Custom 404 handler
@app.error(404)
def not_found(req, res):
res.render('404.html')
# Custom 500 handler
@app.error(500)
def internal_error(req, res):
res.render('500.html')
You can use try-except blocks to handle errors in route handlers.
@app.get('/')
def home(req, res):
try:
# Route handler logic
res.render('index.html')
except Exception as e:
print(f"Error: {str(e)}")
# Send appropriate response based on error
status_code = getattr(e, 'status_code', 500)
message = getattr(e, 'message', str(e))
res.status(status_code).json({
'error': True,
'message': message
})
Expressify provides several utilities to help with common tasks.
app.locals is an object that persists throughout the lifetime of the application. You can use it to store app-level data.
app.locals.title = 'My Application'
app.locals.version = '1.0.0'
app.set is used to set application-level settings like the template engine or the static files directory.
app.set('view engine', 'jinja2')
app.set('views', './views')
app.set('static_folder', 'static')
The request object provides several methods to access information about the request.
Returns the HTTP method of the request.
@app.get('/')
def home(req, res):
res.send(f"Method: {req.method}")
Returns the full URL of the request.
@app.get('/')
def home(req, res):
res.send(f"URL: {req.url}")
Returns a dictionary of request headers.
@app.get('/')
def home(req, res):
res.send(f"Headers: {req.headers}")
Returns a dictionary of query parameters.
@app.get('/search')
def search(req, res):
query = req.query.get('q')
res.send(f'Searching for: {query}')
Returns a dictionary of route parameters.
@app.get('/users/:id')
def get_user(req, res):
user_id = req.params.get('id')
res.send(f'User ID: {user_id}')
Contains the parsed request body.
@app.post('/users')
def create_user(req, res):
user_data = req.body
# Process user data...
res.status(201).json({'message': 'User created', 'user': user_data})
The response object provides several methods to send data back to the client.
Sets the HTTP status code for the response.
@app.get('/')
def home(req, res):
res.status(200).send('OK')
Sets a custom header for the response.
@app.get('/')
def home(req, res):
res.set_header('X-Custom-Header', 'Value')
res.send('OK')
Sets the Content-Type HTTP header for the response.
# Setting different content types
@app.get('/text')
def text(req, res):
res.type('text/plain').send('Plain text content')
@app.get('/html')
def html(req, res):
res.type('text/html').send('HTML Content
')
@app.get('/xml')
def xml(req, res):
res.type('application/xml').send('XML Content ')
@app.get('/css')
def css(req, res):
res.type('text/css').send('body { color: blue; }')
Sends various types of data back to the client.
@app.get('/')
def home(req, res):
res.send('Hello, World!')
Sends JSON data back to the client.
@app.get('/api/data')
def get_data(req, res):
res.json({
'name': 'Example',
'items': [1, 2, 3]
})
Redirects the request to a different path.
@app.get('/old-page')
def old_page(req, res):
# Temporary redirect (302)
res.redirect('/new-page')