Axum Web Tools
General purpose tools for axum web framework.
Usage example with some features
with_txfunction to run SQLX transactions in Axum web framework.Claimsstruct to extract authenticated user from JWT token.HttpErrorstruct to return error responses.okfunction to return successful responses.
[]
= { = "xxx" }
= { = "xxx" }
= { = "xxx" }
= { = "xxxx"}
use State;
use Response;
use ;
use Router;
use with_tx;
use ;
use Claims;
use info;
use ScopedFutureExt;
use Serialize;
use PgPoolOptions;
use PgPool;
use ;
use FromStr;
use endpoint;
pub type Tx<'a> = Transaction;
async
async
async
async
PgSQL Migrate
A powerful PostgreSQL migration tool included with axum-webtools that provides database schema management with advanced features for complex operations.
Installation
Install the migration tool binary:
Basic Usage
# Create a new migration
# Run all pending migrations
# Run migrations with specific environment (default: prod)
# Rollback migrations (rollback 1 migration by default)
# Rollback specific number of migrations
# Rollback with specific environment
# Baseline existing migrations (mark as applied without running)
Migration Files
Migrations are created as pairs of .up.sql and .down.sql files:
migrations/
├── 000001_create_users_table.up.sql
├── 000001_create_users_table.down.sql
├── 000002_add_indexes.up.sql
├── 000002_add_indexes.down.sql
└── 000003_create_materialized_views.up.sql
└── 000003_create_materialized_views.down.sql
Advanced Features
1. No Transaction Feature (no-tx)
Some PostgreSQL operations cannot run within transactions. Use the no-tx feature for operations like:
CREATE INDEX CONCURRENTLYCREATE MATERIALIZED VIEWALTER TYPE ADD VALUE
Example:
-- features: no-tx
-- This migration runs without a transaction wrapper
(email);
-- Multiple materialized views in the same script
CREATE MATERIALIZED VIEW user_stats AS
SELECT
DATE(created_at) as date,
COUNT(*) as user_count
FROM users
GROUP BY DATE(created_at);
CREATE MATERIALIZED VIEW daily_activity AS
SELECT
DATE(last_login) as login_date,
COUNT(*) as active_users
FROM users
WHERE last_login IS NOT NULL
GROUP BY DATE(last_login);
2. Split Statements Feature (split-statements)
When you need to execute multiple complex operations that require separate execution contexts, use the split-statements feature with markers:
Example:
-- features: split-statements
-- First block: Create base tables
-- split-start
(
id SERIAL PRIMARY KEY,
name VARCHAR(100) NOT NULL
);
INSERT INTO categories (name) VALUES
('Electronics'),
('Books'),
('Clothing');
-- split-end
-- Second block: Create dependent materialized view
-- split-start
CREATE MATERIALIZED VIEW category_stats AS
SELECT
c.name,
COUNT(p.id) as product_count
FROM categories c
LEFT JOIN products p ON p.category_id = c.id
GROUP BY c.id, c.name;
-- Create indexes on the materialized view
(name);
-- split-end
-- Third block: Grant permissions
-- split-start
SELECT ON category_stats TO readonly_user;
ALL ON categories TO app_user;
-- split-end
3. Skip On Environment Feature (skip-on-env)
Skip specific SQL blocks based on the current environment. This feature works at the block level within split statements, allowing fine-grained control over which blocks execute in different environments.
Use the --env or -e CLI parameter to specify the current environment (default: prod).
Example: Skip seed data blocks in production
-- features: split-statements
-- Block 1: Schema changes (runs in all environments)
-- split-start
(
id SERIAL PRIMARY KEY,
email VARCHAR(255) NOT NULL
);
-- split-end
-- Block 2: Seed data (skip in production)
-- split-start
-- skip-on-env prod
INSERT INTO users (email) VALUES
('dev@example.com'),
('test@example.com');
-- split-end
-- Block 3: More schema changes (runs in all environments)
-- split-start
(email);
-- split-end
Example: Skip performance optimizations in dev/homolog
-- features: no-tx, split-statements
-- Block 1: Basic index (runs everywhere)
-- split-start
(user_id);
-- split-end
-- Block 2: Heavy index (skip in dev and homolog)
-- split-start
-- skip-on-env dev,homolog
(created_at, status, total);
-- split-end
Running with environment:
# Run in dev environment - blocks with "-- skip-on-env dev" will be skipped
# Run in production (default) - blocks with "-- skip-on-env prod" will be skipped
# Run in homolog environment
4. Combined Features
You can combine features for complex scenarios:
Example: Multiple materialized views without transactions
-- features: no-tx, split-statements
-- First materialized view block
-- split-start
CREATE MATERIALIZED VIEW hourly_sales AS
SELECT
DATE_TRUNC('hour', created_at) as hour,
SUM(total_amount) as total_sales,
COUNT(*) as order_count
FROM orders
GROUP BY DATE_TRUNC('hour', created_at);
-- split-end
-- Second materialized view block
-- split-start
CREATE MATERIALIZED VIEW product_performance AS
SELECT
p.id,
p.name,
COUNT(oi.id) as times_sold,
SUM(oi.quantity) as total_quantity
FROM products p
LEFT JOIN order_items oi ON oi.product_id = p.id
GROUP BY p.id, p.name;
-- split-end
-- Concurrent indexes block
-- split-start
(hour);
(times_sold DESC);
-- split-end
Migration Tracking
The tool automatically:
- Creates a
pgsql_migrate_schema_migrationstable to track applied migrations - Stores content hashes to detect changes in already-applied migrations
- Marks migrations as "dirty" during execution to handle failed migrations
- Validates migration integrity before execution
Error Handling
- Dirty migrations: If a migration fails, it's marked as dirty and must be manually resolved
- Content changes: Warns when applied migration content has changed
- Validation: Ensures proper marker pairing in split-statements feature
- Transaction safety: Automatically handles transaction wrapping based on features
Use Cases
Perfect for:
- Database schema evolution with complex dependencies
- Creating multiple materialized views that need separate execution contexts
- Concurrent index creation without blocking operations
- Data migrations that require multi-step processing
- Permission management across multiple database objects
- Performance optimizations that need specific execution patterns
Example: Complex E-commerce Migration
-- features: no-tx, split-statements
-- Create core product tables
-- split-start
(
id SERIAL PRIMARY KEY,
name VARCHAR(100) NOT NULL,
parent_id INTEGER REFERENCES product_categories(id)
);
(
id SERIAL PRIMARY KEY,
category_id INTEGER NOT NULL REFERENCES product_categories(id),
name VARCHAR(255) NOT NULL,
price DECIMAL(10,2) NOT NULL,
created_at TIMESTAMP DEFAULT NOW
);
-- split-end
-- Create performance materialized views
-- split-start
CREATE MATERIALIZED VIEW category_hierarchy AS
WITH RECURSIVE cat_tree AS (
SELECT id, name, parent_id, 0 as level, ARRAY[id] as path
FROM product_categories WHERE parent_id IS NULL
UNION ALL
SELECT c.id, c.name, c.parent_id, t.level + 1, t.path || c.id
FROM product_categories c
JOIN cat_tree t ON c.parent_id = t.id
)
SELECT * FROM cat_tree;
-- split-end
-- Create concurrent indexes for performance
-- split-start
(category_id, price DESC);
(created_at DESC);
-- split-end
This comprehensive migration system ensures reliable, trackable, and flexible database schema management for complex applications.