0Pricing
Learn Rust Coding · Aula

Integração com banco de dados

Persista dados

Integração com banco de dados é uma aula grátis de Learn Rust Coding no CoddyKit. Esta é a aula 3 de 4. 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 4 aulas no total.

Persistindo dados

Até agora, nossa API armazenava dados na memória, que desaparecem ao reiniciar. Serviços reais precisam de um banco de dados. Nesta lição, conectaremos uma API Axum ao PostgreSQL usando o sqlx, um conjunto de ferramentas SQL assíncrono para Rust, com verificação em tempo de compilação.

Você aprenderá sobre pools de conexão, consultas, mapeamento de linhas para estruturas e uso do pool como estado compartilhado.

Adicionando sqlx

Adicione o sqlx com os recursos necessários: um ambiente de execução, TLS e um driver de banco de dados. Abaixo está uma configuração do Postgres usando Tokio.

// Cargo.toml
// [dependencies]
// sqlx = { version = "0.7", features = [
//   "runtime-tokio", "tls-rustls", "postgres", "macros"
// ] }

O pool de conexões

Abrir uma nova conexão para cada requisição é lento. Um pool de conexões mantém um conjunto de conexões reutilizáveis. PgPoolOptions constrói um pool a partir de uma URL de banco de dados. O pool é barato de clonar (internamente, ele usa contagem de referências), o que o torna ideal como estado compartilhado.

use sqlx::postgres::PgPoolOptions;

async fn make_pool(url: &str) -> sqlx::PgPool {
    PgPoolOptions::new()
        .max_connections(5)
        .connect(url)
        .await
        .expect("failed to connect")
}

Pool como estado da aplicação

Passe o pool ao Axum com .with_state(pool). Os manipuladores então recebem State(pool): State<PgPool>. Como clonar o pool é barato, cada requisição compartilha as mesmas conexões subjacentes.

use axum::{routing::get, Router};
use sqlx::PgPool;

fn build(pool: PgPool) -> Router {
    Router::new()
        .route("/todos", get(list_todos))
        .with_state(pool)
}

Executando uma consulta

A função sqlx::query executa SQL bruto. Vincule os parâmetros com .bind(value) para evitar injeção de SQL; o Postgres usa os marcadores $1 e $2. Use .execute(&pool) para gravações que não retornam linhas.

use sqlx::PgPool;

async fn insert_todo(pool: &PgPool, title: &str) -> Result<(), sqlx::Error> {
    sqlx::query("INSERT INTO todos (title, done) VALUES ($1, $2)")
        .bind(title)
        .bind(false)
        .execute(pool)
        .await?;
    Ok(())
}

Mapeando linhas para estruturas

Derive sqlx::FromRow no seu modelo para que os resultados das consultas sejam mapeados diretamente para ele. Use query_as::<_, Todo> com fetch_all para obter um Vec<Todo>, ou fetch_one para uma única linha.

use sqlx::{PgPool, FromRow};

#[derive(FromRow, serde::Serialize)]
struct Todo { id: i32, title: String, done: bool }

async fn all_todos(pool: &PgPool) -> Result<Vec<Todo>, sqlx::Error> {
    let rows = sqlx::query_as::<_, Todo>("SELECT id, title, done FROM todos")
        .fetch_all(pool)
        .await?;
    Ok(rows)
}

Um manipulador que lê o banco de dados

Combine as partes: um manipulador obtém o pool do estado, executa uma consulta e retorna JSON. Mapeie os erros do banco de dados para o status 500, para que o cliente receba uma resposta adequada em vez de um pânico.

use axum::{extract::State, Json, http::StatusCode};
use sqlx::PgPool;

async fn list_todos(
    State(pool): State<PgPool>,
) -> Result<Json<Vec<Todo>>, StatusCode> {
    match all_todos(&pool).await {
        Ok(todos) => Ok(Json(todos)),
        Err(_) => Err(StatusCode::INTERNAL_SERVER_ERROR),
    }
}

Retornando linhas inseridas

O Postgres pode retornar a linha que acabou de inserir com RETURNING. Combine-o com query_as e fetch_one para obter o novo registro, incluindo seu id gerado, em uma única viagem de ida e volta.

use sqlx::PgPool;

async fn create(pool: &PgPool, title: &str) -> Result<Todo, sqlx::Error> {
    let todo = sqlx::query_as::<_, Todo>(
        "INSERT INTO todos (title, done) VALUES ($1, false) \
         RETURNING id, title, done")
        .bind(title)
        .fetch_one(pool)
        .await?;
    Ok(todo)
}

Migrações

Seu esquema deve existir antes da execução das consultas. O sqlx oferece suporte a migrações: arquivos SQL em uma pasta migrations/, aplicados em ordem. Execute-as na inicialização com sqlx::migrate!() para configurar automaticamente um banco de dados novo.

use sqlx::PgPool;

async fn run_migrations(pool: &PgPool) {
    sqlx::migrate!("./migrations")
        .run(pool)
        .await
        .expect("migrations failed");
}
// migrations/0001_init.sql contains the CREATE TABLE statements.

Transações

Quando várias gravações precisam ter sucesso juntas, envolva-as em uma transação. Comece com pool.begin(), execute as consultas usando o identificador da transação e, em seguida, faça commit. Se você descartar a transação sem confirmar, o sqlx desfará as alterações automaticamente, mantendo os dados consistentes.

use sqlx::PgPool;

async fn transfer(pool: &PgPool) -> Result<(), sqlx::Error> {
    let mut tx = pool.begin().await?;
    sqlx::query("UPDATE accounts SET balance = balance - 10 WHERE id = 1")
        .execute(&mut *tx).await?;
    sqlx::query("UPDATE accounts SET balance = balance + 10 WHERE id = 2")
        .execute(&mut *tx).await?;
    tx.commit().await?;
    Ok(())
}

Configuração e segredos

Nunca coloque as credenciais do banco de dados diretamente no código. Leia DATABASE_URL do ambiente, geralmente carregado de um arquivo .env com o crate dotenvy durante o desenvolvimento. Em produção, a plataforma o injeta como uma variável de ambiente.

use std::env;

async fn connect_from_env() -> sqlx::PgPool {
    let url = env::var("DATABASE_URL")
        .expect("DATABASE_URL must be set");
    make_pool(&url).await
}

Verificação rápida

Teste sua compreensão da integração com bancos de dados.

Recapitulação

Você integrou um banco de dados:

  • Use um pool de conexões PgPool e compartilhe-o por meio de .with_state.
  • Use query/execute para gravações e query_as com FromRow para leituras tipadas.
  • Sempre vincule os parâmetros para evitar injeção de SQL.
  • RETURNING busca as linhas inseridas; as transações agrupam gravações de forma atômica.
  • Execute as migrações na inicialização e leia DATABASE_URL do ambiente.

Perguntas Frequentes

A aula “Integração com banco de dados” é grátis?

Sim — o texto completo de “Integração com banco de dados” é 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 4 aulas no total.

O que vou aprender em “Integração com banco de dados”?

Persista dados 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 3 de 4.

Quanto tempo leva a aula “Integração com banco de dados”?

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. Configuração do projeto
  2. Endpoints e modelos
  3. Integração com banco de dados
  4. Teste da API
← Voltar para Learn Rust Coding