Skip to content
Tutorials

Inspect local data with Studio

You have created users and posts. Now look at the rows without writing another query. Point Studio at your local project database:

If you need one first, scaffold it with rent new, add models with rent init User Post Comment, and apply them with rent migrate dev --name initial. An in-memory SQLite database does not survive its process, so use the project's rent.db for browsing:

rent studio

Choose users, posts, or comments in the sidebar. Studio shows the catalog-derived columns and primary key, then loads the first page of rows. It starts read-only so exploration cannot accidentally change the tutorial data.

To edit local development data, stop Studio and opt into editing:

rent studio --allow-writes

Double-click a cell and enter its new value as JSON. For example, a string must include JSON quotes:

"A clearer post title"

Studio sends a parameterized update constrained by the row's full primary key and accepts success only when exactly one row changes.

For permission-aware browsing, continue with Studio viewer mode.

Complete example

Download this project, or copy the files below into an empty directory.

Replace RENT_REPOSITORY_URL in Cargo.toml with the URL supplied with your preview access. Use the matching Rent CLI release.

Create a persistent database, add a post, then open Studio:

export DATABASE_URL='sqlite://lesson.db?mode=rwc'
rent generate
rent migrate dev --name initial
cargo run
rent studio
Cargo.toml
[package]
name = "rent-lesson-21-studio"
version = "0.1.0"
edition = "2024"

[workspace]

[[bin]]
name = "rent-lesson-21-studio"
path = "main.rs"

[dependencies]
rent = { git = "RENT_REPOSITORY_URL", tag = "v0.8.6", default-features = false, features = ["sqlite", "migrate", "extensions"] }
anyhow = "1.0.104"
tokio = { version = "1.53.1", features = ["full"] }
tempfile = "3.27.0"

# Used by the generated client after `rent generate`.
[package.metadata.cargo-machete]
ignored = ["rent"]
rent.toml
schema = "schema.rsl"
generated = "generated"
migrations = "migrations"

[database]
dialect = "sqlite"
backends = ["sqlite"]
schema.rsl
datasource db {
  provider = "sqlite"
}

model User {
  id       BigInt     @id
  name     String
  posts    Post[]
  comments Comment[]  @relation("CommentAuthor")
  profile  Profile?

  @@map("users")
}

model Post {
  id        BigInt     @id
  title     String
  body      String
  published Boolean
  author_id BigInt
  author    User       @relation(fields: [author_id], references: [id])
  comments  Comment[]

  @@map("posts")
}

model Comment {
  id        BigInt  @id
  body      String
  post_id   BigInt
  author_id BigInt
  post      Post    @relation(fields: [post_id], references: [id], onDelete: Cascade)
  author    User    @relation("CommentAuthor", fields: [author_id], references: [id])

  @@map("comments")
}

model Profile {
  id      BigInt  @id
  bio     String
  user_id BigInt  @unique
  user    User    @relation(fields: [user_id], references: [id], onDelete: Cascade)

  @@map("profiles")
}
main.rs
use anyhow::Result;
#[allow(dead_code, unused_imports)]
#[rustfmt::skip]
#[path = "generated/mod.rs"]
mod generated;
async fn example(url: &str) -> Result<()> {
    let client = generated::client::Client::connect(url).await?;
    let maya = client
        .upsert_user(generated::user::unique::id)
        .id(1)
        .name("Maya")
        .save()
        .await?;
    client
        .upsert_post(generated::post::unique::id)
        .id(1)
        .title("Welcome")
        .body("Open this post in Studio")
        .published(false)
        .author_id(maya.id)
        .save()
        .await?;
    Ok(())
}
#[tokio::main]
async fn main() -> Result<()> {
    example(&std::env::var("DATABASE_URL")?).await
}
#[cfg(test)]
mod test_support;
#[cfg(test)]
mod tests {
    #[tokio::test]
    async fn example_runs() -> anyhow::Result<()> {
        let (_directory, url) = super::test_support::database()?;
        super::example(&url).await
    }
}
test_support.rs
use anyhow::{Context, Result, ensure};
use std::process::Command;

pub fn database() -> Result<(tempfile::TempDir, String)> {
    let directory = tempfile::tempdir()?;
    std::fs::write(directory.path().join("schema.rsl"), include_str!("schema.rsl"))?;
    std::fs::write(directory.path().join("rent.toml"), r#"schema = "schema.rsl""#)?;
    let url = format!("sqlite://{}?mode=rwc", directory.path().join("app.db").display());
    let executable = std::env::var("RENT_CLI").unwrap_or_else(|_| "rent".into());
    let output = Command::new(executable)
        .arg("migrate")
        .arg("dev")
        .arg("--root")
        .arg(directory.path())
        .arg("--to")
        .arg("schema.rsl")
        .arg("--database-url")
        .arg(&url)
        .output()
        .context("run the Rent CLI")?;
    ensure!(
        output.status.success(),
        "migration failed: {}",
        String::from_utf8_lossy(&output.stderr)
    );
    Ok((directory, url))
}

On this page