Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Your First API

📋 See test

Let’s build a task manager. We’ll start simple and add features as we need them.

Setup

cargo new taskmanager
cd taskmanager

Add dependencies to Cargo.toml:

[package]
name = "taskmanager"
version = "0.1.0"
edition = "2021"

[dependencies]
crudcrate = "0.1"
sea-orm = { version = "1.0", features = ["runtime-tokio-rustls", "sqlx-sqlite"] }
axum = "0.7"
tokio = { version = "1", features = ["full"] }
serde = { version = "1.0", features = ["derive"] }

The Simplest Task

Replace src/main.rs:

use axum::Router;
use crudcrate::EntityToModels;
use sea_orm::{entity::prelude::*, Database, DatabaseConnection};

#[derive(Clone, Debug, DeriveEntityModel, EntityToModels)]
#[crudcrate(generate_router)]
#[sea_orm(table_name = "tasks")]
pub struct Model {
    #[sea_orm(primary_key)]
    #[crudcrate(primary_key)]
    pub id: i32,

    pub title: String,
}

#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
pub enum Relation {}

impl ActiveModelBehavior for ActiveModel {}

#[tokio::main]
async fn main() {
    let db: DatabaseConnection = Database::connect("sqlite::memory:")
        .await
        .expect("Database connection failed");

    // Create table
    db.execute(sea_orm::Statement::from_string(
        db.get_database_backend(),
        "CREATE TABLE tasks (id INTEGER PRIMARY KEY, title TEXT NOT NULL)".to_owned(),
    ))
    .await
    .expect("Table creation failed");

    let app = Router::new()
        .merge(task_router())
        .layer(axum::Extension(db));

    println!("Task Manager running at http://localhost:3000");
    let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap();
    axum::serve(listener, app).await.unwrap();
}

Run It

cargo run

Try It

# Create a task
curl -X POST http://localhost:3000/tasks \
  -H "Content-Type: application/json" \
  -d '{"id": 1, "title": "Learn CRUDCrate"}'

# List tasks
curl http://localhost:3000/tasks

# Get one task
curl http://localhost:3000/tasks/1

# Update it
curl -X PUT http://localhost:3000/tasks/1 \
  -H "Content-Type: application/json" \
  -d '{"title": "Master CRUDCrate"}'

# Delete it
curl -X DELETE http://localhost:3000/tasks/1

That’s it. You have a working REST API.


What We Got

From those few lines, CRUDCrate generated:

EndpointWhat it does
GET /tasksList all tasks
GET /tasks/:idGet one task
POST /tasksCreate a task
PUT /tasks/:idUpdate a task
DELETE /tasks/:idDelete a task

Plus request/response models, error handling, and JSON serialization.


Try the Minimal Example

Don’t want to type all this? Run the included example:

git clone https://github.com/evanjt/crudcrate
cd crudcrate/crudcrate
cargo run --example minimal

Then visit:

  • API: http://localhost:3000/todo
  • Docs: http://localhost:3000/docs (interactive OpenAPI)

But Wait…

Did you notice we had to specify "id": 1 when creating? That’s annoying. Users shouldn’t have to pick their own IDs.

Next: Let’s fix that - make IDs generate automatically.