Building a REST API for Math Operations  (+, *, /) with Rust, Actix, and Rhai๐Ÿฆ€

Building a REST API for Math Operations (+, *, /) with Rust, Actix, and Rhai๐Ÿฆ€

Let's code a program that will perform mathematical operations (+, *, /) using HTTP requests and Rhai in Rust

ยท

6 min read

Introduction

Hello, Rust enthusiasts and friends! ๐Ÿฆ€

Are you ready to embark on another journey in Rust? Today, we'll explore how to create a REST API that performs basic mathematical operations: addition, multiplication, and division. We'll use Actix, a powerful web framework for Rust, together with Rhai, a lightweight scripting language, to achieve our goal.

This tutorial is designed with junior Rust developers in mind (as I'm also new to Rust), so we'll take it step by step, making sure everything is clear and easy to follow. Let's dive in and start coding!

Feel free to show your support on X! ๐Ÿ’™

Setting Up Your Project

First things first, you'll need to set up a new Rust project and we need to add our dependencies to the Cargo.toml file.

[dependencies]
actix-web = "4.0.1"
rhai = "1.6.1"

This will include Actix-web for handling HTTP requests and Rhai for scripting our mathematical operations.

Note: You can find the complete project on GitHub.

Creating the Route Handlers

Open the main.rs file in your project's src directory and let's start coding!

As mentioned in the above section, we'll need to import the necessary modules and traits from Actix and Rhai libraries to handle HTTP requests and execute Rhai scripts.

use actix_web::{HttpServer, get, App, web::Path, Responder};
use rhai::Engine;

Route Handlers: We'll define the 3 route handlers (divide, multiply, add) and use Actix macros. These handlers extract two integer parameters (num1 and num2) from the URL path using the Path extractor.

#[get("/divide/{num1}/{num2}")]
#[get("/multiply/{num1}/{num2}")]
#[get("/add/{num1}/{num2}")]

The code for divide, multiply, add is pretty similar, so let me explain to you how we can build the divide function and you can build accordingly the other too (or check my GitHub if you need more guidance).

#[get("/divide/{num1}/{num2}")]
async fn divide(path: Path<(i64, i64)>) -> impl Responder {
    let (num1, num2) = path.into_inner();

    let mut engine = Engine::new();
    engine.register_fn("num1", move || num1);
    engine.register_fn("num2", move || num2);

    let result = engine.eval_file::<i64>("src/divide.rhai".into()).unwrap();

    format!("{result}")
}
  1. Define an asynchronous route handler for division: The function divide is marked with the #[get("/divide/{num1}/{num2}")] attribute, which tells Actix to call this function when an HTTP GET request is made to the /divide/{num1}/{num2} path. The {num1} and {num2} in the path are placeholders for the two numbers that will be divided.

  2. Extract path parameters: The path: Path<(i64, i64)> argument uses Actix's Path extractor to automatically extract and parse the num1 and num2 path parameters into a tuple of two i64 numbers.

  3. Initialize Rhai Engine: A new Engine instance from the Rhai scripting language is created. This engine will be used to evaluate Rhai scripts.

  4. Register functions in Rhai Engine: The engine.register_fn("num1", move || num1); and engine.register_fn("num2", move || num2); lines register two functions, num1 and num2, in the Rhai engine. These functions, when called from Rhai scripts, will return the values of num1 and num2 that were extracted from the path.

  5. Evaluate Rhai script: The engine.eval_file::<i64>("src/divide.rhai".into()).unwrap(); line tells the Rhai engine to evaluate (execute) the script located in src/divide.rhai (we'll write this a little bit later). This script performs the division operation using the num1 and num2 values. The result of the script evaluation is expected to be an i64 integer, which is the result of the division.

  6. Return the result: Finally, the result of the division is formatted as a string and returned as the HTTP response.

(Similarly, write the code for multiply and add.)

Creating the REST API

  1. Start Actix web server: The #[actix_web::main] attribute marks the main function as the entry point of the web server application. It allows the function to be async, making it possible to use asynchronous programming within.

  2. Create a new HTTP server instance: HttpServer::new() is called to create a new instance of an HTTP server.

  3. Configure the application: Inside the closure passed to HttpServer::new(), App::new() is called to create a new application instance. This application is configured with services (endpoints) that it should provide.

  4. Register services: The .service() method is used to add services to the application. Here, multiply, add, and divide are the names of the functions that handle requests to their respective endpoints.

  5. Bind to an address: .bind(("127.0.0.1", 8080))? tells the HTTP server to listen for incoming connections on the local address 127.0.0.1 (localhost) and port 8080. The ? operator is used for error handling; if binding fails (for example, if the port is already in use), the application will return an error and exit.

  6. Run the server: .run().await starts the HTTP server and awaits its completion. The server runs indefinitely, handling incoming requests according to the services defined, until it's manually stopped or an unrecoverable error occurs.

Here's how the final part looks like:

#[actix_web::main]
async fn main() -> std::io::Result<()> {
    HttpServer::new(|| {
        App::new()
            .service(multiply)
            .service(add)
            .service(divide)
    })
    .bind(("127.0.0.1", 8080))?
    .run()
    .await
}

Rhai Script for Operations

Inside the src directory, create three Rhai script files: add.rhai, multiply.rhai, and divide.rhai.

Here's what the multiply.rhai looks like:

fn multiply(num1, num2) {
    return num1 * num2;
}

let num1 = num1();
let num2 = num2();

multiply(num1, num2);
  1. Define a multiply function: A function named multiply is defined with two parameters num1 and num2. This function performs multiplication of num1 and num2 and returns the result.

  2. Invoke registered functions: The lines let num1 = num1(); and let num2 = num2(); invoke functions named num1 and num2 that have been previously registered in the Rhai engine. These functions return the values of num1 and num2 that were extracted from the request path and passed to the script.

  3. Call the multiply function: Finally, the multiply function is called with the values of num1 and num2 as arguments. The result of this operation is the product of num1 and num2, which is the intended outcome of the script.

The add.rhai and divide.rhai scripts are similar, just replace multiply with the respective operation.

Running the Program and Practical Tips

With everything set up, it's time to run our API. Open your terminal and execute:

cargo run

After the project compiles and starts, open your browser and try accessing something like http://localhost:8080/{operation}/{num1}/{num2}.

Practical Tips:

Conclusion

Congratulations! ๐ŸŽŠ You've just created a REST API in Rust using Actix and Rhai for performing basic mathematical operations. This project showcases the power and flexibility of Rust for web development, as well as the ease of integrating scripting capabilities with Rhai. Happy coding!


๐Ÿ‘‹ Hello, I'm Eleftheria, Community Manager, developer, public speaker, and content creator.

๐Ÿฅฐ If you liked this article, consider sharing it.

๐Ÿ”— All links | X | LinkedIn

Did you find this article valuable?

Support Eleftheria Batsou's Blog by becoming a sponsor. Any amount is appreciated!

ย