Rust API - Health Checker - Part I
Bem, o intuito aqui não é fazer um Hello World, mas fazer um CRUD bem feito com Rust ajudando ter uma base para uma aplicação web com a linguagem e suas ferramentas, não é falar sobre os conceitos básicos da linguagem (mutabilidade, borrow, ownership e etc), mas dar aplicabilidade a ela, nos conceitos básicos tem essa playlist aqui:
Dito isso, eu quero fazer uma simples API em Rust onde você ter o mínimo de conhecimento sobre como fazer. Para começar é importante que você tenha o Cargo. Cargo é uma ferramenta em Rust que irá nos ajudar a desenvolver a nossa aplicação, ao menos a estrutura dela e onde depois iremos adicionar as nossas libs para poder construir a nossa aplicação:
Para instalar é só você se direcionar para esse site aqui:
https://doc.rust-lang.org/cargo/getting-started/installation.html
Lá é só você seguir os baby steps para poder instalar o cargo na sua máquina
Note: Um ponto, tu precisa do rust instalado para instalar o cargo, pode ser óbvio, mas é bom ressaltar o óbvio.
Vamos lá eu vou começar rodando o comando:
cargo new rust-api
Num primeiro momento eu quero só mostrar a estrutura que temos:
└── rust-api
└── target
└── src
└── main.rs
└── Cargo.toml
└── Cargo.lock
Ela é bem simples, se você olhar são poucos folders, o target é um arquivo de binários, src é onde ficará a nossa aplicação e temos o Cargo.toml onde nós temos a definição da nossas libs, um package.json do mundo rust e o Cargo.lock que é onde temos a definição das nossas dependências.
Bem, nessa aplicação iremos usar algumas libs como actix para a nossa parte de requisições http, sqlx para fazer a nossa parte de query junto ao postgres (Ele não é um ORM!), serde para json parse, chrono para datação, dotenv para ler variáveis locais da nossa aplicação e env_logger para log.
Nosso arquivo Cargo.toml vai ficar assim:
[package]
name = "rust-api"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
actix = "0.13.0"
actix-web = "4.2.1"
chrono = { version = "0.4.22", features = ["serde"] }
dotenv = "0.15.0"
serde = { version = "1.0.145", features = ["derive"] }
serde_json = "1.0.86"
env_logger = "0.10.0"
sqlx = { version = "0.6.2", features = ["runtime-async-std-native-tls", "postgres", "chrono", "uuid"] }
uuid = { version = "1.3.0", features = ["serde", "v4"] }
Depois disso é rodar o seguinte comando:
cargo build
para começar vamos criar mais um arquivo dentro de src chamado services.rs e por hora vamos deixar por lá, dentro do arquivo main.rs nós iremos começar a escrever a nossa aplicação
primeiro eu vou começar a importar algumas coisas que serão necessárias na nossa aplicação
use actix_web::{
App,
HttpServer,
};
fn main() {
// restante do código
};
Repare que importamos o App onde nós iremos criar a instância da nossa aplicação e o HttpServer onde nós iremos subir a nossa estrutura,
Primeiro vamos apagar tudo que tem dentro da função, e acima da nossa main vamos declarar que a mesma será uma actix main web application.
#[actix_web::main]
async fn main() -> std::io::Result<()> {
}
Repare que aqui fazemos o uso de uma async function, se você vem do javascript sabe que essa chamada nos ajuda a ter maior sincronicidade em um conjunto de funções assíncronas, assim garantindo a chamada correta dentro das nossas execuções, um outro ponto é que logo em seguida temos a chamada da std::io::Result
para a execução e retorno da nossa main
Nós agora vamos para o nosso arquivo services.rs.
No nosso arquivo nós iremos definir o nosso healthchecker para saber se a nossa aplicação está rodando da forma correta:
use actix_web::{ get, web, HttpResponse, Responder };
use serde_json::json;
#[get("/healthchecker")]
async fn health_checker() -> impl Responder {
const MESSAGE: &str = "Health check: API is up and running smoothly.";
HttpResponse::Ok().json(json!({
"status": "success",
"message": MESSAGE
}))
}
pub fn config(conf: &mut web::ServiceConfig) {
let scope = web::scope("/api").service(health_checker);
conf.service(scope);
}
o código no final é bem simples, fizemos a chamada do método get do actix, junto com o método web para poder criar a nossa função é realizar um escopo por onde a nossa aplicação irá passar e será exportada de forma pública para a nossa main e temos a nossa função healt_checker que é uma resposta do nosso HttpResponse no formato json para o nosso usuário e nisso somente devolvemos a mensagem que a API está funcionando e uma mensagem de sucesso.
Voltando para o nosso arquivo main, nós iremos chamar dentro da nossa aplicação o nosso services.rs
mod services;
// todo código abaixo
o arquivo todo por hora está assim e iremos trabalhar dentro da nossa função main:
mod services;
use actix_web::{
App,
HttpServer,
};
#[actix_web::main]
async fn main() -> std::io::Result<()> {
}
dentro dela ficará assim:
#[actix_web::main]
async fn main() -> std::io::Result<()> {
println!("Server started successfully");
HttpServer::new(move || {
App::new()
.configure(services::config)
})
.bind(("127.0.0.1", 8080))?
.run().await
}
o arquivo todo de main ficará assim:
mod services;
use actix_web::{
App,
HttpServer,
};
#[actix_web::main]
async fn main() -> std::io::Result<()> {
println!("Server started successfully");
HttpServer::new(move || {
App::new()
.configure(services::config)
})
.bind(("127.0.0.1", 8080))?
.run().await
}
e esse é o nosso service.rs em caso de dúvidas:
use actix_web::{ get, web, HttpResponse, Responder };
use serde_json::json;
#[get("/healthchecker")]
async fn health_checker() -> impl Responder {
const MESSAGE: &str = "Health check: API is up and running smoothly.";
HttpResponse::Ok().json(json!({
"status": "success",
"message": MESSAGE
}))
}
pub fn config(conf: &mut web::ServiceConfig) {
let scope = web::scope("/api").service(health_checker);
conf.service(scope);
}
E se você rodar a nossa aplicação com o cargo run
, ela ficará assim:
http://localhost:8080/api/healthchecker
Se quiser o CURL:
curl --request GET \
--url http://localhost:8080/api/healthchecker \
--header 'Content-Type: application/json'
Retorno:
{
"message": "Health check: API is up and running smoothly.",
"status": "success"
}
Até mais! =]