This content originally appeared on DEV Community and was authored by NARUHODO
In this multi-parts guide I will show you how to build a simple API in Rust. If you're not sure what Rust is, it's a low-level programming language created at Mozilla. It provides performances similar to C++ but is much safer thanks to the way its compiler is designed. It can catch many common runtime errors at compile time.
Disclaimer
I myself am a beginner in Rust and there are still lots of things I need to learn. So it is possible that what I will show you might not be the best way to do things. If you notice something odd, please let me know!
Prerequisites
To be able to follow this guide you should at least know the basics of Rust, if not I would advise checking the Rust Book.
You will also need to have Rust and Cargo installed.
What will this guide cover?
This guide is a bit long so I've decided to split it into 3 parts.
The first part will cover the basics, I will explain how to setup the project and create one endpoint that will return a Hello World!
message.
In the second part, I will connect the API to a MongoDB and add two endpoints to create and retrieve documents from the database.
In the third and last part, I will show you how to build an authentication middleware to protect one of the endpoints.
The web framework
I've decided to use tide
as the web framework. It is less popular than actix
(which is the most popular at the time being) but as I come from JavaScript, I feel more at home with tide
as it is a bit similar to express
and overall simpler to use in my opinion.
Project setup
Let's start setting up the project.
First, make sure you have Rust and Cargo installed.
Side-note, cargo
is the package manager for Rust, it is similar to npm
for those who come from JavaScript.
Type the following command to create a new project with cargo
cargo new rust-api-example-part-1
We're now ready to code, it would probably be easier to open this project in your favorite code editor.
If you're using Visual Studio Code, I recommend installing the following extensions:
- rust-analyzer (matklad.rust-analyzer) (VS Code marketplace link)
- Better TOML (bungcip.better-toml) (VS Code marketplace link)
rust-analyzer provides all the useful features when coding in Rust (code completion, linting, documentation, etc)
Better TOML provides syntax highlighting for .toml
files, which is the format used by Cargo for configuration.
Installing the dependencies
Before we start coding, we need to install the dependencies. At this stage we will only need three of them, tide
, async-std
and serde
.
As I explained earlier, tide
is the web framework that we will use to build the API.
async-std
is an async runtime. Rust does not provide an async runtime by default, so it is up to us to install one. At the moment there are two popular options, tokio
and async-std
.
This is a bit annoying because it means that you need to make sure all the asynchronous libraries you will use in the project need to be compatible with the async runtime you picked.
tide
requires that we use async-std
so we will install this runtime.
serde
is a framework to serialize and deserialize data (such as converting JSON objects to Rust structs).
Open the Cargo.toml
file and add the following under [dependencies]
tide = "0.16"
async-std = { version = "1", features = ["attributes"] }
serde = { version = "1.0", features = ["derive"] }
You can specify extra features like I did for async-std
, in this case we will need the attributes
feature.
Make sure it runs
By default when creating the project with cargo, you should have a default src/main.rs
file containing an example function.
Let's make sure it runs with
cargo run
It should print Hello, world!
in your terminal.
Now you can delete that function so we can start setting up our API.
Let's code!
In the src/main.rs
file, add the following
#[async_std::main]
async fn main() -> tide::Result<()> {
let app = tide::new();
app.listen("127.0.0.1:8080").await?;
return Ok(());
}
Our main
function is now asynchronous because we've added the async
keyword in front. For it to work correctly we need to add a runtime, which is exactly what #[async_std::main]
does. It is an "attribute" macro that simply wraps our main
function in an asynchronous runtime.
The function returns tide::Result<()>
which means it will return either nothing (Ok(())
) or a Tide error.
Inside the function, we create a new Tide instance then we call the function listen
to start the local server on the port 8080
.
If you try to contact the API now, you will get a 404
, because we didn't define any route yet.
Let's create our first controller, add the following above the main
function in src/main.rs
use tide::Request;
#[derive(Clone, Debug)]
struct State {}
async fn hello(_req: Request<State>) -> tide::Result {
return Ok("Hello world!".into());
}
The hello
controller is asynchronous and takes one parameter. Since we don't actually use the parameter we can add a leading underscore per convention. The _req
parameter is of type Request
(imported from tide
), it expects a State which is why I've defined an empty State
struct.
The State
struct needs to implement the Clone
trait so we use the derive
marco from serde
to do so.
The controller return type needs to be tide::Result
.
As for the implementation, we simply return a &str
that we transform into a tide::Result
with the .into()
function.
That's all for our controller. Now we will need to make a few changes to our main
function. Change it with the following
#[async_std::main]
async fn main() -> tide::Result<()> {
let mut app = tide::with_state(State {});
app.at("/hello").get(hello);
app.listen("127.0.0.1:8080").await?;
return Ok(());
}
I've modified the initialization of our app to pass an empty State
. This will be useful later when we will need to pass around our database connection.
Then I've added our endpoint GET /hello
.
Now if you try to run the app and make a request to http://localhost:8080/hello
you should get Hello world!
as a response.
In conclusion
That's it for the part 1 of this guide, I hope it was helpful. If you noticed any error or something that could be improved, please let me know!
You can find the the full example on GitHub: https://github.com/ncribt/rust-api-example-part-1
Discover more posts from me on my personal blog: https://naruhodo.dev
This content originally appeared on DEV Community and was authored by NARUHODO

NARUHODO | Sciencx (2021-06-14T07:28:42+00:00) Build an API in Rust (Part 1). Retrieved from https://www.scien.cx/2021/06/14/build-an-api-in-rust-part-1/
Please log in to upload a file.
There are no updates yet.
Click the Upload button above to add an update.