Installation

Installation

On this page

Requirements

  • Rust 1.100 or later. The workspace declares rust-version = "1.100", the toolchain CI builds with and the one every deployment runs, so that is the floor for every feature. The third-party crates the drivers pull in all ask for less (the highest is the AWS SDKs, at 1.94).

  • A database, or nothing at all — SQLite in memory is the default and needs no setup

Start from the sample project

Do not start from an empty crate. rainier-sample-project is a complete, working application with the layout the rest of these pages assume, and it is what the framework is designed around.

git clone https://github.com/safewords/rainier-sample-project.git my-app
cd my-app
cp .env.example .env

cargo run -- app:seed      # a demo user and a few posts
cargo run -- serve         # http://127.0.0.1:8000

That works on a fresh clone with nothing installed, because it runs against SQLite in memory. Point DATABASE_URL at a file, MySQL or Postgres when you want the data to survive — nothing else in the app changes.

Then rename it:

# Cargo.toml
[package]
name = "my-app"
[lib]
name = "my_app"
path = "src/lib.rs"

[[bin]]
name = "my-app"
path = "src/main.rs"

Adding Rainier to an existing crate

[dependencies]
rainier-framework = { git = "https://github.com/safewords/rainier-framework.git", features = [
    "sea-orm-executor",
] }

# The ORM lives in the same repository — it is a workspace member, not a
# separate crate to version against.
rainier-orm = { git = "https://github.com/safewords/rainier-framework.git" }

async-trait = "0.1"
chrono = { version = "0.4", features = ["clock", "serde"] }
serde = { version = "1", features = ["derive"] }
serde_json = "1"
tokio = { version = "1", features = ["rt-multi-thread", "macros", "signal"] }
tracing = "0.1"
use rainier_framework::prelude::*;

async fn index() -> &'static str {
    "Hello from Rainier"
}

#[tokio::main]
async fn main() -> Result<()> {
    let app = Rainier::new(".")
        .with_routes(|router| {
            router.get("/", index).name("home");
        })
        .boot()
        .await?;

    rainier_framework::console("rainier").run_from_env(&app).await;
    Ok(())
}

The two rules that bite everyone once

Both of these produce confusing errors, and both have a one-line fix.

1. Depend on rainier-orm directly

#[derive(Entity)] expands to ::rainier_orm::… — an absolute path. The framework re-exporting Rainier ORM is not enough to satisfy it.

error[E0433]: failed to resolve: use of undeclared crate or module `rainier-orm`

Add it to [dependencies] as shown above. Both must resolve to the same version, which is why the sample project pins both to git and why the lockfile records both.

2. Do not call bind_executor! for an executor you did not write

// Wrong, in an application:
rainier_framework::bind_executor!(rainier_drivers::sql::SeaOrmExecutor);
error[E0117]: only traits defined in the current crate can be implemented
              for types defined outside of the crate

The orphan rule. Connection belongs to rainier-database and SeaOrmExecutor to Rainier ORM, so that impl is out of an application's reach. Rainier ships it behind the sea-orm-executor feature:

rainier-framework = { git = "…", features = ["sea-orm-executor"] }

bind_executor! is for executors you wrote. See Database.

Features

FeatureGives you
sea-orm-executorthe native MySQL / Postgres / SQLite driver
d1-httpCloudflare D1 over HTTP
libsql-httplibSQL / Turso over HTTP

Pick one. All are off by default, which is what keeps the crates usable from a wasm target — an application targeting a Worker drops sea-orm-executor and adds d1-http, and nothing else changes.

Verify it works

cargo run -- list          # the command list
cargo run -- route:list    # the route table
cargo test

route:list printing your routes means the router compiled, the container built every middleware that asked it for a service, and the kernel is bound. If that works, the application is wired.

Editor setup

rust-analyzer will want to build everything once — the first check is slow and every one after it is not.

The workspace is clippy-clean, so it is worth keeping it that way:

cargo clippy --workspace --all-targets

Next

This page is the framework’s docs/installation.md at 7b8cb15. Found a mistake? Edit it on GitHub.

Type to search 2.0.0 docs.