0Pricing
Learn Rust Coding · Aula

Integração com Bases de Dados (SQLx/Diesel)

Integre o seu serviço Web Rust com bases de dados utilizando ORMs ou construtores de consultas populares, como SQLx ou Diesel.

Integração com Bases de Dados (SQLx/Diesel) é uma aula grátis de Learn Rust Coding no CoddyKit. Esta é a aula 2 de 3. Você pode ler a aula completa abaixo gratuitamente — depois pratica ao vivo no navegador com um editor de código integrado e um tutor de IA 24/7. Faz parte do caminho de aprendizado de Learn Rust Coding, e seu progresso é sincronizado entre a web e o app CoddyKit. O curso de Learn Rust Coding inclui 3 aulas no total.

Por que usar bancos de dados na web?

Os serviços web frequentemente precisam armazenar e recuperar dados de forma persistente. Pense em perfis de usuários, catálogos de produtos ou publicações de blog: esses dados precisam existir em algum lugar além da memória do seu programa.

Os bancos de dados oferecem uma maneira estruturada de gerenciar esses dados, garantindo que sejam seguros, consistentes e acessíveis. Integrar seu serviço web Rust a um banco de dados é uma tarefa essencial na maioria das aplicações do mundo real.

SQLx: seu aliado assíncrono para bancos de dados

Em Rust, temos excelentes ferramentas para interagir com bancos de dados. Duas opções populares são SQLx e Diesel.

  • SQLx: Uma biblioteca SQL assíncrona, totalmente escrita em Rust, com consultas verificadas em tempo de compilação. Ela costuma ser a opção preferida para serviços web assíncronos criados com estruturas como Tokio.
  • Diesel: Um ORM (Mapeador Objeto-Relacional) poderoso que oferece uma forma mais abstrata de interagir com o banco de dados, mapeando estruturas Rust diretamente para tabelas do banco. Normalmente, ele é síncrono.

Nesta lição, vamos nos concentrar no SQLx devido ao seu forte suporte assíncrono, que combina bem com o desenvolvimento moderno de serviços web em Rust.

Configuração do SQLx: dependências

Antes de escrever o código, você precisa adicionar sqlx e tokio ao Cargo.toml do seu projeto. tokio fornece o ambiente de execução assíncrono necessário para sqlx.

Por exemplo, para usar SQLite (um banco de dados baseado em arquivo, prático para o desenvolvimento local) com SQLx, seu Cargo.toml poderia ser assim:

[dependencies] sqlx = { version = "0.7", features = ["runtime-tokio-rustls", "sqlite"] } tokio = { version = "1", features = ["full"] }

Normalmente, você também usará uma variável de ambiente DATABASE_URL para configurar a string de conexão com o banco de dados.

Conecte-se ao seu primeiro banco de dados

Vamos estabelecer uma conexão com um banco de dados SQLite na memória. O SQLx usa conjuntos de conexões, que gerenciam várias conexões de banco de dados com eficiência para lidar com solicitações simultâneas no seu serviço web.

Experimente executar este exemplo:

use sqlx::SqlitePool;
use tokio;

#[tokio::main]
async fn main() -> Result<(), sqlx::Error> {
    // Use an in-memory SQLite database for simplicity
    let database_url = "sqlite::memory:"; 
    let pool = SqlitePool::connect(&database_url).await?;
    println!("Successfully connected to the database!");

    // In a real web app, 'pool' would be passed around or stored in app state.

    Ok(())
}

Estruturando dados com tabelas

Antes de podermos armazenar dados, precisamos definir o esquema do banco de dados usando DDL (Linguagem de Definição de Dados). Vamos criar uma tabela tasks simples.

A macro sqlx::query! permite incorporar SQL diretamente ao seu código Rust. Ela também verifica seu SQL em tempo de compilação!

use sqlx::{SqlitePool, query};
use tokio;

#[tokio::main]
async fn main() -> Result<(), sqlx::Error> {
    let database_url = "sqlite::memory:";
    let pool = SqlitePool::connect(&database_url).await?;

    // Create a 'tasks' table if it doesn't exist
    query!(
        "CREATE TABLE IF NOT EXISTS tasks (
            id INTEGER PRIMARY KEY AUTOINCREMENT,
            description TEXT NOT NULL,
            completed BOOLEAN NOT NULL DEFAULT FALSE
        )"
    ).execute(&pool).await?;

    println!("Table 'tasks' created successfully!");
    Ok(())
}

Adicionando novos registros

Agora que temos uma tabela, vamos adicionar alguns dados! Usaremos a instrução INSERT com vinculação de parâmetros. Isso protege contra injeção de SQL e torna suas consultas mais seguras.

Observe que os espaços reservados ? são usados para os parâmetros, e os valores são passados para .bind().

use sqlx::{SqlitePool, query};
use tokio;

#[tokio::main]
async fn main() -> Result<(), sqlx::Error> {
    let database_url = "sqlite::memory:";
    let pool = SqlitePool::connect(&database_url).await?;

    query!(
        "CREATE TABLE IF NOT EXISTS tasks (
            id INTEGER PRIMARY KEY AUTOINCREMENT,
            description TEXT NOT NULL,
            completed BOOLEAN NOT NULL DEFAULT FALSE
        )"
    ).execute(&pool).await?;

    // Insert a new task
    let description = "Learn SQLx";
    let completed = false;
    query!(
        "INSERT INTO tasks (description, completed) VALUES (?, ?)",
        description,
        completed
    ).execute(&pool).await?;

    println!("Task '{}' inserted successfully!", description);
    Ok(())
}

Recuperando dados com SELECT

Para buscar dados da nossa tabela tasks, usamos a instrução SELECT. O SQLx fornece métodos como .fetch_all() ou .fetch_one() para obter os resultados.

Você pode percorrer as linhas retornadas e acessar as colunas pelo nome ou pelo índice.

use sqlx::{SqlitePool, query};
use tokio;

#[tokio::main]
async fn main() -> Result<(), sqlx::Error> {
    let database_url = "sqlite::memory:";
    let pool = SqlitePool::connect(&database_url).await?;

    query!(
        "CREATE TABLE IF NOT EXISTS tasks (
            id INTEGER PRIMARY KEY AUTOINCREMENT,
            description TEXT NOT NULL,
            completed BOOLEAN NOT NULL DEFAULT FALSE
        )"
    ).execute(&pool).await?;

    query!(
        "INSERT INTO tasks (description, completed) VALUES (?, ?)",
        "Learn SQLx", false
    ).execute(&pool).await?;
    query!(
        "INSERT INTO tasks (description, completed) VALUES (?, ?)",
        "Build a web app", false
    ).execute(&pool).await?;

    // Fetch and print all tasks
    println!("\n--- All Tasks ---");
    let rows = query!("SELECT id, description, completed FROM tasks")
        .fetch_all(&pool).await?;

    for row in rows {
        println!("ID: {}, Desc: {}, Done: {}", 
            row.id, row.description, row.completed);
    }
    Ok(())
}

Dados com segurança de tipos usando estruturas

Extrair dados manualmente das linhas pode causar erros. A macro query_as! do SQLx, combinada com #[derive(FromRow)], permite mapear os resultados das consultas diretamente para estruturas Rust!

Isso oferece uma forte segurança de tipos e torna seu código muito mais limpo e fácil de usar.

use sqlx::{FromRow, SqlitePool, query, query_as};
use tokio;

#[derive(Debug, FromRow)]
struct Task {
    id: i64,
    description: String,
    completed: bool,
}

#[tokio::main]
async fn main() -> Result<(), sqlx::Error> {
    let database_url = "sqlite::memory:";
    let pool = SqlitePool::connect(&database_url).await?;

    query!(
        "CREATE TABLE IF NOT EXISTS tasks (
            id INTEGER PRIMARY KEY AUTOINCREMENT,
            description TEXT NOT NULL,
            completed BOOLEAN NOT NULL DEFAULT FALSE
        )"
    ).execute(&pool).await?;

    query!(
        "INSERT INTO tasks (description, completed) VALUES (?, ?)",
        "Learn SQLx", false
    ).execute(&pool).await?;

    // Fetch tasks directly into our Task struct
    println!("\n--- Tasks as Structs ---");
    let tasks = query_as::<_, Task>("SELECT id, description, completed FROM tasks")
        .fetch_all(&pool).await?;

    for task in tasks {
        println!("{:?}", task);
    }
    Ok(())
}

Modificando e excluindo registros

Atualizar e excluir dados segue um padrão semelhante ao da inserção. Você usa as instruções SQL UPDATE e DELETE com vinculação de parâmetros.

  • UPDATE: Altera as linhas existentes com base em uma cláusula WHERE.
  • DELETE: Remove linhas com base em uma cláusula WHERE.

Tenha sempre cuidado com as cláusulas WHERE em UPDATE e DELETE para evitar a perda indesejada de dados!

use sqlx::{SqlitePool, query};
use tokio;

#[tokio::main]
async fn main() -> Result<(), sqlx::Error> {
    let database_url = "sqlite::memory:";
    let pool = SqlitePool::connect(&database_url).await?;

    query!(
        "CREATE TABLE IF NOT EXISTS tasks (
            id INTEGER PRIMARY KEY AUTOINCREMENT,
            description TEXT NOT NULL,
            completed BOOLEAN NOT NULL DEFAULT FALSE
        )"
    ).execute(&pool).await?;

    query!(
        "INSERT INTO tasks (description, completed) VALUES (?, ?)",
        "Finish lesson", false
    ).execute(&pool).await?;
    let task_id_to_update = 1;

    // Update a task
    query!(
        "UPDATE tasks SET completed = ? WHERE id = ?",
        true,
        task_id_to_update
    ).execute(&pool).await?;
    println!("Task {} updated.", task_id_to_update);

    // Delete a task (uncomment to run, careful with IDs after update)
    // let task_id_to_delete = 1;
    // query!(
    //     "DELETE FROM tasks WHERE id = ?",
    //     task_id_to_delete
    // ).execute(&pool).await?;
    // println!("Task {} deleted.", task_id_to_delete);

    Ok(())
}

Teste seus conhecimentos sobre bancos de dados

Quais das afirmações a seguir são TRUE a respeito do SQLx em serviços web Rust?

Integração com bancos de dados: principais conclusões

Você deu um passo importante na criação de serviços web Rust robustos ao aprender sobre integração com bancos de dados!

  • Persistência: Os bancos de dados são essenciais para armazenar os dados da aplicação.
  • SQLx: Uma biblioteca SQL assíncrona, verificada em tempo de compilação e excelente para serviços web.
  • Conjuntos de conexões: Essenciais para o acesso eficiente e simultâneo ao banco de dados.
  • CRUD: Abordamos a criação (INSERT), a recuperação (SELECT), a atualização (UPDATE) e a exclusão (DELETE) de dados.
  • Segurança de tipos: query_as! e #[derive(FromRow)] permitem um mapeamento de dados seguro e prático.

Em seguida, você pode explorar a integração dessas operações de banco de dados em rotas web reais!

Perguntas Frequentes

A aula “Integração com Bases de Dados (SQLx/Diesel)” é grátis?

Sim — o texto completo de “Integração com Bases de Dados (SQLx/Diesel)” é grátis para ler aqui na web. Para praticá-la interativamente (um editor de código integrado e um tutor de IA 24/7) e desbloquear o restante do curso de Learn Rust Coding, atualize para CoddyKit PRO. O curso de Learn Rust Coding inclui 3 aulas no total.

O que vou aprender em “Integração com Bases de Dados (SQLx/Diesel)”?

Integre o seu serviço Web Rust com bases de dados utilizando ORMs ou construtores de consultas populares, como SQLx ou Diesel. Você pratica Learn Rust Coding com código prático que executa diretamente no navegador, e um tutor de IA 24/7 responde suas dúvidas enquanto trabalha na aula.

Preciso ter experiência prévia para começar Learn Rust Coding?

Nenhuma experiência prévia é necessária. Learn Rust Coding no CoddyKit é estruturado para alunos iniciantes até avançados, então você pode começar aqui ou desde o início e aprender no seu ritmo. Esta é a aula 2 de 3.

Quanto tempo leva a aula “Integração com Bases de Dados (SQLx/Diesel)”?

A maioria das aulas CoddyKit leva cerca de 5–10 minutos. Cada uma é compacta e interativa, então você faz progresso constante e retoma exatamente de onde parou entre web e app.

Posso escrever e executar código nesta aula de Learn Rust Coding?

Sim. Cada aula de Learn Rust Coding inclui um editor de código integrado, então você escreve e executa código real direto no navegador e recebe feedback de IA instantaneamente — nenhuma configuração local necessária.

Todas as aulas deste curso

  1. APIs REST com Actix-web/Rocket
  2. Integração com Bases de Dados (SQLx/Diesel)
  3. Autenticação e Autorização
← Voltar para Learn Rust Coding