Description: https://images.manning.com/360/480/resize/book/9/03ac487-c409-4b45-ac49-8affc8b524fe/Eshwarla-RSSA-MEAPHI.png

From Rust Servers, Services, and Apps by Prabhu Eshwarla

This article shows you how you can get started using Actix and Rust.


Take 40% off Rust Servers, Services, and Apps by entering fcceshwarla into the discount code box at checkout at manning.com.


This article is organized into two parts. We’ll build a barebones HTTP server and API to understand the foundational concepts of Actix. Along the way, you’ll be introduced to key Actix concepts such as routes, handlers, HTTP request parameters and HTTP responses.

In this section we’ll write our first Actix server, which can respond to an HTTP request.

Let’s write some code, shall we?

Writing the first REST API

In this section we’ll write out the first barebones Actix server, which can respond to an HTTP request.

A note about the environment

You can organize code in many ways in this article.

The first option is to create a workspace project, and create separate projects under the workspace.

The second option is to create a separate cargo binary project. Grouping options for deployment can be determined at a later time.

Either approach is fine, but in this article, we adopt the first approach to keep things organized together. We’ll create a workspace project – ezytutors which will hold other projects.

Create a new project with

 
 cargo new ezytutors && cd ezytutors
  

This creates a binary cargo project. Let’s convert this into a workspace project. Under this workspace, let’s store individual web and other backend services.

Add the following to Cargo.toml:

 
 [workspace]
 members = ["tutor-nodb"]
  

tutor-nodb is the name of the webservice we create in this article. Create another cargo project as follows:

 
 cargo new tutor-nodb && cd tutor-nodb
  

This creates a binary Rust project called tutor-nodb under the ezytutors workspace. For convenience, we call this tutor web service henceforth. The root folder of this cargo project contains src subfolder and Cargo.toml file.

Add the following dependencies in Cargo.toml of tutor web service:

 
 [dependencies]
 actix-web = "3.1.0"                                1
 actix-rt = "1.1.1"                                 2
  

1 You can use this version of actix-web or whichever later version is available at the time you are reading this.

2 Async run-time for Actix. Rust requires use of external run-time engine for executing async code.

Add the following binary declaration to the same Cargo.toml file, to specify the name of the binary file.

 
 [[bin]]
 name = "basic-server"
  

Let’s now create a source file called basic-server.rs under the /src/bin folder. This contains the main() function which is the entry point for the binary.

Four basic steps are needed to create and start a basic HTTP server in Actix:

  • Configure routes: Routes are paths to various resources in a web server. For our example, we configure a route /health to do health checks on the server
  • Configure handler: Handler is the function that handles requests for a route. We define a health-check handler to service the /health route.
  • Construct a web application and register routes and handlers
  • Construct an HTTP server linked to the web application and run the server

These four steps are shown in code with annotations. Add the following code to src/bin/basicserver.rs. Don’t worry if you don’t understand all the steps and code; type it in for now.

Listing 1: Writing a basic Actix web server

 
 // Module imports
 use actix_web::{web, App, HttpResponse, HttpServer, Responder};
 use std::io;
  
 // Configure route                                                     1
 pub fn general_routes(cfg: &mut web::ServiceConfig) {
     cfg.route("/health", web::get().to(health_check_handler));
 }
  
 //Configure handler                                                    2
 pub async fn health_check_handler() -> impl Responder {
     HttpResponse::Ok().json("Hello. EzyTutors is alive and kicking")
 }
  
 // Instantiate and run the HTTP server
 #[actix_rt::main]
 async fn main() -> io::Result<()> {
     // Construct app and configure routes                              3
     let app = move || App::new().configure(general_routes);
  
     // Start HTTP server                                               4
     HttpServer::new(app).bind("127.0.0.1:3000")?.run().await
 }
  

1 For HTTP GET requests coming in on route /health, the Actix web server routes

the request to health_check_handler()

2 The handler constructs an HTTP Response with a greeting

3 Construct an Actix web application instance and register the configured routes.

4 Initialize a web server, load the application, bind it to a socket and run the server.

You can run the server in one of two ways.

If you’re in workspace folder root, run the following command:

 
 cargo run -p tutor-nodb --bin basic-server
  

The -p flag tells cargo tool to build and run the binary for project tutor-nodb, within the workspace.

Alternatively, you can run the command from within the tutor-nodb folder (tutor web service) as follows:

 
 cargo run --bin basic-server
  

In a browser window, visit the following URL:

 
 localhost:3000/health
  

You can see the following printed:

 
 Hello, EzyTutors is alive and kicking
  

Congratulations! You built your first REST API in Actix.

Understanding Actix concepts

In the previous section, we wrote a basic Actix web server (aka Actix HTTP server). The server was configured to handle a single route /health which returns the health status of the server. Figure 1 shows the various components of Actix that we used in the code.


Figure 1: Actix Basic server


Here is the sequence of steps:

  1. When you typed localhost:3000/health in your browser, an HTTP GET request message was constructed by the browser, and sent to the Actix basic-server listening at localhost:3000 port.
  2. The Actix basic-server inspected the GET request and determined the route in the message to be /health. The basic server routed the request to the web application (App) that has the /health route defined.
  3. The web application in turn determined the handler for the route /health to be health_check_handler() and routed the message to the handler.
  4. The health_check_handler() constructs an HTTP response with a text message and sends it back to the browser.

You would have noticed the terms HTTP server, Web Application, Route and Handler used prominently. These are key concepts within Actix to build web services. Let us understand each of them in more detail, and their exact role within the Actix web framework:

HTTP (web) Server: It’s responsible for serving HTTP requests. It understands and implements the HTTP protocol. By default, the HTTP server starts a number of threads (called workers) to process incoming requests. The default number of threads is equal to the number of logical CPUs in the system.

The HTTP server is built around the concept of web applications and requires one for initialization. It constructs an application instance for each thread. An HTTP server can have several web applications running concurrently, each bound to a different port.

App: This represents an Actix web application. An Actix web application is a grouping of the set of routes it can handle.

Routes and handlers A route in Actix tells the Actix web server how to process an incoming request.

A route is defined in terms of a route path, an HTTP method and a handler function. Said differently, a request handler is registered with an application’s route on a path for a particular HTTP method. The structure of an Actix route is illustrated in figure here.



This is the route we implemented earlier for health check:

 
 cfg.route(
     "/health",
     web::get()
     .to(health_check_handler));
  

1 Path

2 HTTP method

3 Request handler method

The route shown above specifies that if an GET HTTP request arrives for the path /health, the request should be routed to the request handler method health_check_handler().

A request handler is an asynchronous method that accepts zero or more parameters and returns an HTTP response.

The following is a request handler that we implemented in the previous example.

 
 pub async fn health_check_handler() -> impl Responder {
     HttpResponse::Ok().json("Hello, EzyTutors is alive and kicking")
 }

In code shown, health_check_handler() is a function that implements Responder trait. Types that implement Responder trait acquire the capability to send HTTP responses. Our handler doesn’t accept any input parameter.

Listed here are a few more details about the Actix web framework.

Actix-web is a modern, rust-based, light-weight and fast web framework. Actix-web has consistently featured among the best web frameworks in TechEmpower performance benchmarks, which can be found here: https://www.techempower.com/benchmarks/. Actix-web is among the most mature Rust web frameworks and supports many features.

Let’s look at a few more features of the Actix web framework.

  • Support for HTTP/1.x and HTTP/2
  • Support for request and response pre-processing
  • Middleware can be configured for features such as CORS, session management, logging, and Authentication
  • It supports asynchronous I/O. This provides the ability for the Actix server to perform other activities while waiting on network I/O.
  • Content compression
  • Can connect to multiple databases
  • Provides an additional layer of testing utilities over Rust built-in testing framework to support testing of HTTP requests and responses
  • Supports static web hosting and server-rendered templates

More technical details about the Actix web framework can be found here: https://docs.rs/crate/actixweb/2.0.0

Using a framework like Actix-Web significantly speeds up the time for prototyping and development of web APIs in Rust, as it takes care of the low-level details of dealing with HTTP protocols and messages, and provides several utility functions and features to make web application development easier. We’ll use Actix-Web in this article to develop a set of RESTful web service apis.

Actix-web has an extensive feature set and we’ll be able to cover only a subset of the features in this article. The features that we cover include HTTP methods that provide CRUD (Create-ReadUpdate-Delete) functionality for resources, persistence with databases, error handling, state management, JWT authentication, and configuring middleware.

If you want to learn more, check out the book on Manning’s liveBook platform here.