Building a web site in Rust

rust, cargo, axum, and tokio

Erik
Erik
40 min read
Cover Image for Building a web site in Rust

Introduction

Today, we will be building:

  1. A simple web server using Rust
  2. A simple website served from this web server
  3. An improved version of this website using web components

We will start by setting up a new Rust project using Cargo, the Rust package manager, and then build a simple web server using Axum. Once the web server is working as we expect, we'll extend this to make a simple set of web components to serve a user-driven application.

Getting Started with Rust

Rust is a systems programming language that focuses on performance, safety, and concurrency. It is designed to be fast, reliable, and secure, making it an excellent choice for building web applications at scale. Before we dive into building a web server, let's start by setting up the Rust toolchain and creating a new project using Cargo.

Installation and Setup

The first step to getting started with Rust is to install the Rust toolchain. You can install Rust using the official installer available at rustup.rs. Rustup is a toolchain installer for Rust that makes it easy to install and manage multiple versions of Rust on your system.

Once you have installed Rust, you can verify the installation by running the following command in your terminal:

rustc --version

This command should print the installed version of Rust on your system. If you see the version number, then Rust has been installed successfully.

At the time of this post, my installed version is

rustc 1.76.0

Create a New Project Using Cargo

Cargo is Rust's package manager and build system. It makes it easy to manage Rust projects, including building, testing, and packaging. Cargo also handles dependencies, making it easy to add external libraries to your project. To create a new Rust project, you can use the cargo new command:

cargo new my-web-server

This command will create a new directory called my-web-server with a basic Rust project structure. Inside the my-web-server directory, you will find the following files:

  • Cargo.toml: The manifest file that describes the project and its dependencies.
  • src/main.rs: The main entry point for the Rust application.

Building the Server

Before we can get building the server, we need to install some basic dependencies. We will be using the axum and tokio libraries to build our web server. We can add these dependencies to the Cargo.toml file.

Add Axum and Tokio Dependencies

Run the following command to add the axum and tokio dependencies to the Cargo.toml file:

cargo add axum
cargo add tokio --features full

After this completes, your Cargo.toml file should look something like this:

[package]
name = "my-web-server"
version = "0.1.0"
edition = "2021"
publish = false

[dependencies]
axum = "0.7.4"
tokio = { version = "1", features = ["full"] }

Axum

Axum Web Framework - Github

Axum is a web framework built on top of hyper, a fast low-level HTTP implementation for Rust. Axum provides a powerful and ergonomic API for building asynchronous web applications. It is designed to be composable, allowing you to build complex applications by combining smaller, reusable components.

Axum uses Rust's async/await syntax to handle asynchronous operations, making it easy to write efficient and scalable web applications. It also provides a rich set of features, including routing, middleware, error handling, and more. Axum uses tokio as its asynchronous runtime, making it a great choice for building high-performance web applications.

Tokio

Tokio - Github

Tokio is an asynchronous runtime for Rust that provides a set of tools for building asynchronous applications. It includes an event loop, task scheduler, and other miscellaneous utilities for working with asynchronous I/O.

Define our Entry Point

The src/main.rs file is the main entry point for the Rust application. Open the src/main.rs file in a text editor and add a function to run the web server:

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

#[tokio::main]
async fn main() {
    // build our application with a single route
    let app = Router::new().route("/", get(|| async { "Hello, World!" }));

    // run our app with hyper, listening globally on port 3000
    let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap();
    axum::serve(listener, app).await.unwrap();
}

In this example, we are creating a simple web server that responds with "Hello, World!" for GET /. We are using the axum and tokio dependencies to define the web server and run it asynchronously.

Run the Web Server

To run the web server, use the following command:

cargo run

Let's see how we did. Run the following curl command to test the server:

curl http://localhost:3000/
> Hello World!

Great, it looks like our server is working! We now have an HTTP handler responding to requests.

Awesome! Now, let's make our website a bit more interesting. We'll start by adding a simple HTML page to our server, enhancing the response with a bit of CSS and JavaScript as we go.

Building the Website

Let's start by adding a basic web page to our server.