Database Migrations in Rust: Overview of Diesel, SeaORM, and sqlx Tools
Managing database schema changes is one of the key tasks in developing any web application. In the Rust ecosystem, several mature tools exist for this purpose. In this article, we will look at three main approaches to migrations: Diesel, SeaORM, and sqlx. Each offers its own unique approach to database schema versioning, and choosing the right tool can significantly impact the project's architecture.
1. Diesel — Classic ORM with Migration Integration
Diesel is a safe, extensible ORM and Query Builder for Rust that is tightly integrated with a migration system. Its main advantage is compile-time type checking: table structures are generated based on the database schema, eliminating type mismatch errors.
Migrations in Diesel are plain SQL files organized in the migrations/ directory. Each migration contains two files: up.sql (applying changes) and down.sql (reverting).
Example of a migration structure for creating a users table:
-- migrations/2024-01-15-120000_create_users/up.sqlCREATE TABLE users ( id SERIAL PRIMARY KEY, username VARCHAR(255) NOT NULL UNIQUE, email VARCHAR(255) NOT NULL UNIQUE, created_at TIMESTAMP NOT NULL DEFAULT NOW());
-- migrations/2024-01-15-120000_create_users/down.sqlDROP TABLE IF EXISTS users;The CLI tool diesel_cli is used to run migrations:
# Installationcargo install diesel_cli --no-default-features --features postgres
# Run migrationsdiesel migration run
# Revert the last migrationdiesel migration revert
# Generate a new migrationdiesel migration generate create_usersPros of Diesel:
- Full compile-time type checking
- Simple and clear migration structure (pure SQL)
- Built-in support for rollbacks
- Excellent performance
Cons:
- Requires installing an additional CLI tool
- Complex setup for projects with multiple databases
- Less flexibility compared to sqlx
2. SeaORM — Async ORM with a Flexible Migration System
SeaORM (based on sea-query and sqlx) is an async ORM that offers a more modern approach to migrations. Unlike Diesel, migrations in SeaORM are written in Rust rather than SQL, allowing you to leverage the full power of the language to describe schema changes.
Example of a migration using SeaORM:
use sea_orm_migration::prelude::*;
#[derive(DeriveMigrationName)]pub struct Migration;
#[async_trait::async_trait]impl MigrationTrait for Migration { async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> { manager .create_table( Table::create() .table(Users::Table) .if_not_exists() .col( ColumnDef::new(Users::Id) .integer() .not_null() .auto_increment() .primary_key(), ) .col(ColumnDef::new(Users::Username).string().not_null()) .col(ColumnDef::new(Users::Email).string().not_null()) .col( ColumnDef::new(Users::CreatedAt) .timestamp() .default(Expr::current_timestamp()), ) .to_owned(), ) Also in library
Kotlin WebSocket Libraries for Creating Real-Time in Web Applications
gRPC in Rust: Tonic and Grpc-rs Libraries with Examples
Rate Limiting in Go (Golang): Libraries, Patterns, and Implementation Examples