The Rust language has been gaining tracking for a lot of reasons in the last few years. One of them is the excellent performance and strong high-level constructs provided by Rust's zero-cost abstractions paradigm. Safety and efficiency are traits expected from all Rust frameworks.
You might think that safety and efficiency is something you want from your next webserver application. You might also think Rust language, being lower level than our usual web server go-to languages, might be complicated or convoluted. That's where the developers of Rocket come in. They wanted to marry developer ergonomics to the benefits coming from the language itself so you have no choice but to choose Rocket as the web framework for your next project.
We're starting from scratch, installing rust and rocket and building a server that greets you. The intention here is to introduce the Rocket framework for beginner Rust developers and show the door of web applications in the Rust language.
What You Will Learn#
Setting up your Rust compiler for Rocket
Booting your first Rocket server
Adding a route that takes a parameter and greets you
What You Should Know#
Having familiarity with the Rust language
Knowing the basic concepts of a web server
First thing first: we need to get Rust running. I said we're starting from scratch and I meant it. To get started with our project, we need rustup and cargo. If you already have Rust installed in your system, you can skip this section.
Rustup is the Rust toolchain manager. It's used to install and update the compiler and plug-ins like the official formatter and the official language server. Cargo is the Rust package manager, used to manage projects and dependencies.
To get them both properly installed, follow these official instructions. These installers are very friendly to beginners and you should be done in no time. To verify it's all up and running type
rustup --version and
cargo --version. If you see their versions, we're good to go.
Here's an example of my current outputs.
Now having rustup and cargo working, we can start our project. Cargo has a handy method to get us started:
cargo new --bin rusty-greeter. This command will create a new folder containing an executable project called
--bin parameter for the
cargo new command tells
cargo we want the project to be executable. Conversely, we might have wanted to create a library to be imported by other projects, which would then require the
--lib parameter instead.
Getting inside the project and running
cargo run should execute the project, which will consist just of a Hello World print for now.
Next, we're going to install the rocket package. If you're tired of all the setup, bear with me for just one more section. The project should contain an auto-generated
Cargo.toml file. The required packages are declared there under the dependencies bracket.
That means we want to add the
rocket crate with the particular
0.4.4 version to our project. One more thing though: the
rocket crate requires the nightly compiler to run, so we need to tweak one more option with the
rustup override set nightly command. After that, we can run
cargo build to see if it's all working fine.
I removed the multiple "Downloaded" and "Compiling" lines from my output, but if you saw something like the above output you're ready to go!
You might be curious about the nightly compiler. It is the most up-to-date compiler rust can offer, including all unstable experiments the development team is doing up to last night. To learn more about what it is and what's different about it, check out this section of the official book.
Getting to Hello World#
All right, the setup is done and solid. We want to have an example
GET endpoint that sends from the server a "Hello World" string, just so we know it's all working. Our whole code will be written on the
main.rs file, which should also have been auto-generated by cargo. Open up the file in our favorite editor and replace its contents with this.
Let's break down this code, which is the core of the rocket application.
This line is required by rocket to compile. It's a kind of declaration to the rust compiler what kind of experimental features we want to enable on our project. Don't think about this line too hard, it's just required by rocket currently so we do it and move on.
This line just imports some functions from rocket to make our code cleaner. The first one, get, is a macro that defines a
GET route. The second one, ignite, is the function that creates the rocket application instance. The last one, routes, is a macro that groups all our routes so they can be bundled into the application. Don't worry about understanding them all now, just pay attention to how they are used in the code.
We're using the
get macro along with a regular rust function to define, respectively, a route and a handler for it. This bit of code is what the route is and does: the request of a
GET on the
/greet path will return a "Hello, world!" string.
Finally, the regular Rust main function is populated with the ignite, mount and launch function calls, which are just what rocket needs to start running. The important parts of this code are: the
"/" string, which is the root path of the server; and
routes macro, which should just list all routes in our server.
Testing the Hello World#
We have all we need to see the first response from our server. To run the server with the current code, just run
cargo run. This command builds the project if needed, so don't worry about building it yourself. After a successful compilation, you should see something like this.
That's awesome! You can already see it's all running and with some emojis to make our day better. So how do we try it? You can just open up
http://localhost:8000/greet in your browser or use an http client to try it from terminal. Here's an output from my favourite one.
If you're curious about the log coming from rocket on boot, you can check out the documentation about it. It will be useful when you want to change default configuration.
Getting to Hello You#
We already have the
greet endpoint that greets the world, and all the setup that comes with it. We're half-way there now, we just need to take a parameter so we can greet you instead of the world. Thankfully for us, Rocket makes it easy to do so.
Adding a router parameter to a route in Rocket requires no more imports than what we already have, we just have to pass an extra parameter to the
get macro and take the parameter in the function handler like so.
Let's once again break down the differences here.
Where we previously had the
/greet string literal, we now have a
/greet/<name>. This just tells Rocket we are expecting something to follow the "greet" on the route path, and we want that something to be named "name".
Then we take that "name" as a parameter on the handler, limiting to
Strings only. The Rocket framework will make sure only Strings pass into the handler. We also replaced the previous
&str return type for a
String one, which just means we're going to return a new entire string instead of a literal one.
Lastly, this format macro call constructs the response we want from the parameter. This macro is very handy for simple string manipulation as we're doing right now.
If you got confused about the String vs &str debacle, check out this short blog post about it. Don't worry about it though, it's a regular source of confusion for Rust beginners.
Testing the Hello You#
You are probably getting the hang of it already, but we need to stop running the previously running server (a control+C should suffice) and run it again. Unfortunately, Rocket does not provide us with an automatic code reload. After a new
cargo run, we should see this again.
Also again, we can hit it up on the browser at
http://localhost:8000/greet/Lucas or with an http client, which will output something like this.
You might be wondering so I'm just going to confirm it. I'm Lucas, that's why I tested with my name. I want to be greeted, just like you.
If you're curious, you might have tried the previous example, without the
/<name> parameter, only to find out we broke it. On the browser, it will show an error page.
It's not found! This happened because we told Rocket that the name should be a String. Rust is very strict on its typing system so if told it's a String, it won't accept anything else. Since we want to support both the "Hello World" and the "Hello Lucas" paths, what should we do?
It's surprisingly simple. If we want both behaviors to be present, you just have to write them both as two separate routes. That's what you're
main.rs will look like when totally ready.
There's a couple routes now, with both behaviours. They should have different function names, so Rust can tell them apart, and they should both be added to the
routes macro. Re-running the server again with
cargo run we can see they both are working!
We made it, we have an up-and-running Rust server with a greeting route! Reaching here, you learned a couple of things.
You installed Rustup and Cargo, along with the Rust compiler
You created the project and installed rocket on it
You added two greeting routes that work with a dynamic "name" parameter
You saw it all live in your browser!
Of course, this is just an introductory post to servers in Rust. The intention here was to just break the ice and show that Rust can have good developer ergonomics even with higher-level applications, like webservers. Rocket paved the road and we walked it together.
If you want to keep the learning going, I'd suggest you to start getting more complex with the webserver. Try to answer some of these questions while you keep learning Rocket and Rust.
How would you add a
POSTroute to your server?
How would you keep an internal state in your server?
How would you send and receive
JSONon the server?
The Rocket documentation will be an invaluable help on your learning, but just by writing it yourself, you'll get familiar with the concept. I hope I have helped to get you started, thanks for the reading!