Building a Rust RESTful API

A Step-by-Step Guide

Gathering Insight
5 min read2 days ago
Photo by Fotis Fotopoulos on Unsplash

Creating a fast, efficient RESTful API in Rust involves following a clear process and using the right tools. In this guide, we’ll show you how to set up your development environment with VS Code, containerize your app with Docker, and build your API using the Axum framework. We’ll also cover key libraries, testing strategies, and deploying to cloud platforms like AWS, ECS, or Kubernetes. Whether you’re an experienced Rust developer or just starting out, this guide will help you build a solid, production-ready API.

1. Setting Up Your Development Environment

Choosing Your IDE: VS Code
Visual Studio Code is a great option for Rust development because it’s lightweight, customizable, and has strong community support. Start by installing VS Code and adding the Rust Analyzer extension from the marketplace. This extension provides useful features like syntax highlighting, autocompletion, and error checking. For debugging, you can also install CodeLLDB or the Rust Debugger extension.

Using Docker for Consistency
While developing, you can build and run your code directly in VS Code using Cargo. However, to ensure your app works consistently across environments, Docker is a great tool for simulating production settings. You can use Docker to test your API in an environment that matches the one it will run on in production. For everyday development, though, running cargo run in the integrated terminal works just fine.

To install Docker, download it from Docker’s official website or use your package manager (like apt, yum, or Homebrew). Also, make sure your Rust toolchain is up to date by running:

curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
rustup update

2. Selecting the Right Framework: Axum

For building your RESTful API, Axum is an excellent choice. It’s lightweight, high-performance, and built on Tokio and Hyper, making it perfect for async operations. Axum supports async/await, type safety, and scalability, which makes it an ideal fit for APIs that need to handle multiple requests at once. We chose Axum over other frameworks like Actix or Rocket because it strikes a great balance between simplicity and performance.

3. Essential Libraries for Your API

Here are some libraries you’ll need to enhance your Axum API:

  • Serialization/Deserialization
    Use Serde and Serde-JSON to easily convert Rust data structures to and from JSON, which is the standard for REST APIs.
  • HTTP Client
    Reqwest is a reliable HTTP client for making external requests or testing your API. It supports async operations.
  • Database Access
    Sqlx is a Rust SQL toolkit that supports async operations with databases like PostgreSQL, MySQL, and SQLite. It offers compile-time query checking for safety.
  • Authentication/Authorization
    JSON Web Tokens (JWT) can be used to secure your API endpoints and handle user authentication.
  • Logging and Monitoring
    Use Log and Env-Logger for logging during development. For production, consider using Sentry-Rust to track errors.
  • Configuration
    The Config library allows you to manage API settings (like database URLs and API keys) from environment variables or config files.
  • Utilities
    Tower-HTTP helps with middleware like CORS and rate limiting. You can also use Prometheus to collect performance metrics.

Here’s an updated Cargo.toml example:

[package]
name = "my_rest_api"
version = "0.1.0"
edition = "2021"
[dependencies]
axum = "0.7"
tokio = { version = "1.0", features = ["full"] }
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
sqlx = { version = "0.6", features = ["sqlite", "runtime-tokio-native-tls"] }
log = "0.4"
env_logger = "0.9"
jsonwebtoken = "8.0"
config = "0.13"
tower-http = { version = "0.4", features = ["cors", "rate-limiting"] }
governor = "0.5"
prometheus = { version = "0.13", optional = true }
[dev-dependencies]
reqwest = { version = "0.11", features = ["stream"] }
axum-test-helper = "0.3"
mockall = "0.11"
proptest = "1.0"

4. Building Your RESTful API in VS Code

Start by creating your project in VS Code:

cargo new my_rest_api --bin
cd my_rest_api

In the src/main.rs file, set up your API using Axum. Here’s an example with error handling and middleware:

use axum::{routing::get, Router, response::IntoResponse, http::StatusCode, middleware};
use std::net::SocketAddr;
use log::info;
use tower_http::cors::{CorsLayer, AllowOrigin};
async fn hello_world() -> impl IntoResponse {
"Hello, World!"
}
#[tokio::main]
async fn main() {
env_logger::init();
info!("Starting API server");
let cors = CorsLayer::new()
.allow_origin(AllowOrigin::any());
let app = Router::new()
.route("/", get(hello_world))
.layer(cors)
.layer(middleware::from_fn(|req, next| async {
info!("Request: {:?}", req);
next.run(req).await
}));
let addr = SocketAddr::from(([0, 0, 0, 0], 3000));
info!("Listening on {}", addr);
axum::Server::bind(&addr)
.serve(app.into_make_service())
.await
.unwrap_or_else(|e| {
error!("Server failed: {}", e);
std::process::exit(1);
});
}

Run your app locally by using cargo run or the Run and Debug panel in VS Code. Pay attention to any Rust borrow checker errors, which the IDE will highlight.

5. Comprehensive Testing Strategies

It’s important to test your API to make sure everything is working as expected:

  • Unit Tests
    Test individual functions in src/. Example:
#[cfg(test)]
mod tests {
use super::*;
    #[test]
fn test_hello_world() {
let response = hello_world().await;
assert_eq!(response, "Hello, World!");
}
}
  • Integration Tests
    In the tests/ directory, test your routes and components using axum-test-helper:
// tests/api_test.rs
use my_rest_api::main as create_app;
use axum::http::StatusCode;
use axum_test_helper::{TestClient, TestServer};
#[tokio::test]
async fn test_hello_world() {
let app = create_app();
let server = TestServer::new(app).unwrap();
let client = TestClient::new(server);
let response = client.get("/").send().await;
assert_eq!(response.status(), StatusCode::OK);
assert_eq!(response.text().await, "Hello, World!");
}
  • End-to-End Tests
    Simulate external requests with reqwest:
// tests/e2e_test.rs
use reqwest;
#[tokio::test]
async fn test_api_endpoint() {
let client = reqwest::Client::new();
let response = client.get("http://localhost:3000/")
.send()
.await
.unwrap();
assert_eq!(response.status(), 200);
assert_eq!(response.text().await.unwrap(), "Hello, World!");
}
  • Property-Based Tests
    Use proptest to check input ranges:
use proptest::proptest;
proptest! {
#[test]
fn test_valid_port(port in 1..65535u16) {
assert!(port > 0 && port < 65536);
}
}

6. Packaging Your API

To prepare your API for deployment:

  • Cargo Packaging
    Build a release binary with:
cargo build --release

You’ll find the binary in target/release/my_rest_api.

  • Docker Packaging
    Use a multi-stage build for efficiency:
FROM rust:1.75 as builder
WORKDIR /usr/src/app
COPY . .
RUN cargo build --releaseFROM alpine:3.18COPY --from=builder /usr/src/app/target/release/my_rest_api /app/WORKDIR /app
EXPOSE 3000
CMD ["./my_rest_api"]

Build the Docker image with:

docker build -t my_rest_api .

Test it locally by running:

docker run -d -p 3000:3000 my_rest_api

7. Deploying to Cloud Platforms

To deploy your Dockerized API:

  • AWS (ECS or EKS)
    Push your image to Amazon ECR:
aws ecr get-login-password --region <your-region> | docker login --username AWS --password-stdin <account-id>.dkr.ecr.<your-region>.amazonaws.com
docker tag my_rest_api <account-id>.dkr.ecr.<your-region>.amazonaws.com/my_rest_api:latest
docker push <account-id>.dkr.ecr.<your-region>.amazonaws.com/my_rest_api:latest

Create an ECS task and service through the AWS console or CLI.

  • Kubernetes
    Deploy to a Kubernetes cluster using kubectl or Helm.

Conclusion

This guide walks you through building, testing, and deploying a Rust RESTful API using VS Code, Axum, Docker, and cloud platforms. By focusing on the right tools and frameworks, you’ll create an API that’s fast, secure, and scalable. Regularly check for updates on libraries and cloud services to keep your project up to date.

--

--

Gathering Insight
Gathering Insight

Written by Gathering Insight

A place to leave my understandings and correlations from my notes.

No responses yet