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
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}")
}
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.Extract path parameters: The
path: Path<(i64, i64)>
argument uses Actix'sPath
extractor to automatically extract and parse thenum1
andnum2
path parameters into a tuple of twoi64
numbers.Initialize Rhai Engine: A new
Engine
instance from the Rhai scripting language is created. This engine will be used to evaluate Rhai scripts.Register functions in Rhai Engine: The
engine.register_fn("num1", move || num1);
andengine.register_fn("num2", move || num2);
lines register two functions,num1
andnum2
, in the Rhai engine. These functions, when called from Rhai scripts, will return the values ofnum1
andnum2
that were extracted from the path.Evaluate Rhai script: The
engine.eval_file::<i64>("src/divide.rhai".into()).unwrap();
line tells the Rhai engine to evaluate (execute) the script located insrc/divide.rhai
(we'll write this a little bit later). This script performs the division operation using thenum1
andnum2
values. The result of the script evaluation is expected to be ani64
integer, which is the result of the division.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
Start Actix web server: The
#[actix_web::main]
attribute marks themain
function as the entry point of the web server application. It allows the function to beasync
, making it possible to use asynchronous programming within.Create a new HTTP server instance:
HttpServer::new()
is called to create a new instance of an HTTP server.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.Register services: The
.service()
method is used to add services to the application. Here,multiply
,add
, anddivide
are the names of the functions that handle requests to their respective endpoints.Bind to an address:
.bind(("127.0.0.1", 8080))?
tells the HTTP server to listen for incoming connections on the local address127.0.0.1
(localhost) and port8080
. 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.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);
Define a
multiply
function: A function namedmultiply
is defined with two parametersnum1
andnum2
. This function performs multiplication ofnum1
andnum2
and returns the result.Invoke registered functions: The lines
let num1 = num1();
andlet num2 = num2();
invoke functions namednum1
andnum2
that have been previously registered in the Rhai engine. These functions return the values ofnum1
andnum2
that were extracted from the request path and passed to the script.Call the
multiply
function: Finally, themultiply
function is called with the values ofnum1
andnum2
as arguments. The result of this operation is the product ofnum1
andnum2
, 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:
For example, with
http://localhost:8080/multiply/5/3
you should see the result of the multiplication operation, or withhttp://localhost:8080/add/8/
4
the result of the adding operation.Use ctrl + c (on Windows) to stop the program
To kill a port you can use something like: sudo kill -9
sudo lsof -t -i:8080
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.