Rust - Variáveis de Ambiente no Axum

Fala, pessoal! Vamos dar continuidade à nossa série sobre como criar uma API com Rust e Axum. Hoje, o foco é nas variáveis de ambiente. Bora entender como usá-las na prática?

O que são variáveis de ambiente?

As variáveis de ambiente são pares de chave e valor usados para armazenar informações acessíveis por programas e processos do sistema operacional. Elas servem para configurar o ambiente de execução de um software sem precisar mexer no código-fonte.

Criando uma variável de ambiente

No terminal digite:

export MY_SYSTEM_VARIABLE="Sistema"

Aqui, export define a variável no ambiente atual.

Para verificar se ela foi criada:

echo $MY_SYSTEM_VARIABLE

💡 Observação: Essa variável só existirá no terminal em que foi criada. Se você abrir outro terminal, ela não estará disponível. Portanto, execute cargo run no mesmo terminal em que a variável de ambiente foi criada.

Acessando a variável no código

Vamos ao main.rs importar o módulo necessário:

use std::env;

Agora, crie uma função para buscar o valor da variável:

async fn get_system_env() -> String {
    match env::var("MY_SYSTEM_VARIABLE") {
        Ok(value) => value,
        Err(_) => String::from("Variável não encontrada."),
    }
}

Essa função retorna uma String com o valor da variável, usando env::var para buscá-la. Se não encontrar, retorna Variável não encontrada.

Criando a rota

Vamos adicionar essa rota no nosso servidor Axum:

#[tokio::main]
async fn main() {
    let app = Router::new()
        .route("/", get(get_test))
        .route("/system_env", get(get_system_env))
        .route("/", post(post_test));

    let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap();
    axum::serve(listener, app).await.unwrap();
}

Agora execute o projeto:

cargo run

Testando a rota com cURL:

curl -X 'GET' 'http://127.0.0.1:3000/system_env'

Você verá:

Sistema

Usando um arquivo .env

O problema de definir variáveis diretamente no terminal é que temos que criá-las antes de execurar a aplicação, em ambiente de desenvolvimento, é mais prático centralizar tudo em um arquivo .env.

Na raiz do projeto (junto com Cargo.toml) crie um arquivo .env e adicione:

MY_RUST_VARIABLE="Arquivo"

Agora instale a crate dotenvy:

cargo add dotenvy

No main.rs, importe a crate:

use dotenvy::dotenv;

E crie uma função para ler as variáveis do arquivo .env:

async fn get_env() -> String {
    dotenv().ok();

    match env::var("MY_RUST_VARIABLE") {
        Ok(value) => value,
        Err(_) => String::from("Variável não encontrada."),
    }
}

Essa função carrega as variáveis do .env e busca o valor desejado.

Adicione a rota:

#[tokio::main]
async fn main() {
    let app = Router::new()
        .route("/", get(get_test))
        .route("/system_env", get(get_system_env))
        .route("/env", get(get_env))
        .route("/", post(post_test));

    let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap();
    axum::serve(listener, app).await.unwrap();
}

Execute o projeto e teste:

curl -X 'GET' 'http://127.0.0.1:3000/env'

O retorno será:

Arquivo

💡 Importante: Se a variável existir no sistema e no .env, o valor do sistema será priorizado.

Melhorando com uma struct

Para evitar código repetitivo, podemos criar uma struct para centralizar as variáveis de ambiente:

pub struct Config {
    pub my_rust_variable: String,
}

Aqui, criamos a struct Config com um campo my_rust_variable.

Agora, vamos criar impl para carregar as informações:

impl Config {
    pub fn new() -> Self {
        Config {
            my_rust_variable: env::var("MY_RUST_VARIABLE")
                .expect("Variável não encontrada."),
        }
    }
}

Essa implementação usa expect para simplificar a leitura.

Por fim, criamos a função que retorna o valor:

async fn get_struct_env() -> String {
    dotenv().ok();

    let config = Config::new();
    config.my_rust_variable
}

E adicionamos a rota no main:

#[tokio::main]
async fn main() {
    let app = Router::new()
        .route("/", get(get_test))
        .route("/system_env", get(get_system_env))
        .route("/env", get(get_env))
        .route("/struct_env", get(get_struct_env))
        .route("/", post(post_test));

    let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap();
    axum::serve(listener, app).await.unwrap();
}

Teste com:

curl -X 'GET' 'http://127.0.0.1:3000/struct_env'

Retorno:

Arquivo

Com isso, cobrimos o básico para trabalhar com variáveis de ambiente no Rust. Mas perceba que nosso main.rs está crescendo bastante. Deixar tudo nesse arquivo não é uma boa prática e dificulta a manutenção.

Na próxima semana, vamos organizar nosso projeto em módulos para separar as responsabilidades.

Valeu por acompanhar até aqui! Qualquer dúvida, deixa nos comentários. Fique com Deus!

🔗 Link para a postagem no Linkedin

🔗 Confira o código no GitHub.