It’s 1:00 AM (🎵 soft jazz music). You’re lying in bed trying to get some sleep. The city lights shine into your bedroom, softly illuminating the wall in front of you. You can hear the rain outside and the sound of people going about their late-night business. You can’t fall asleep. Something is bothering you, and you feel like you have to get it off your chest. You have to tell someone, but you don’t want to do it on social media. You want to spill your guts but remain completely anonymous. If only there was a place you could do this…
This is the setting we are going to use for the project of this post — Late Night Confessions: A 🦀 Rust-based website built using Rocket (web framework), Diesel (ORM), and Askama (template rendering engine).
The site functionality is pretty simple:
Since we want to keep our confessions anonymous, we will not create a signup or login form.
Our site has three types of requests to handle:
All confessions will be saved in a Postgres database instance. We will use Diesel to manage the database activities.
Our finished project will look something like this:
The full source code can be found on Github.
We begin by creating a new crate for our project called late-night-rocket:
cargo new late-night-rocket
Next, let’s add Rocket and Failure as dependencies to our Cargo.toml:
[dependencies]
rocket = { git = "https://github.com/SergioBenitez/Rocket", version = "0.5.0-dev" }
failure = {version = "0.1.8", features = ["derive"] }
🔭 Why do we need to specifically specify the 0.5.0-dev version? Since we want our app to run on a stable Rust release, 0.5.0-dev is (as of writing this post) the only version that supports stable Rust.
Rocket makes heavy use of macros. This means our first order of business is to import all of them to our crate. We do this by adding the macrouse attribute to the top of our _main.rs file, pointing to the Rocket crate:
#[macro_use]
extern crate rocket;
Next, let’s add some use statements to import modules we would need:
use rocket::request::Form;
use rocket::response::status::NotFound;
use rocket::response::NamedFile;
use rocket::Rocket;
use std::path::PathBuf;
Finally, it’s time to create some routes!
#[get("/")]
async fn root() -> Result<NamedFile, NotFound<String>> {
NamedFile::open("site/static/index.html")
.await
.map_err(|e| NotFound(e.to_string()))
}
🔬 So what do we have here?
2. Next, static_files, as the name suggests, will handle requests for static files, such as images:
#[get("/<path..>")]
async fn static_files(path: PathBuf) -> Result<NamedFile, NotFound<String>> {
let path = PathBuf::from("site").join(path);
NamedFile::open(path).await.map_err(|e| NotFound(e.to_string()))
}
The route looks very similar to the root route, with one noticeable difference; unlike the staticroot route, this route is dynamic, meaning it matches anything after the / as the path variable. We then use this path variable and append it to the site folder (line 3) before trying to get the file (using NamedFile, just like in the previous route).
3. Next, we mount the new routes to a Rocket instance using the mount method:
fn rocket() -> Rocket {
rocket::ignite().mount("/", routes![root, static_files])
}
🔬So what do we have here?
We ignite (pun intended) a new Rocket instance, then we mount the routes using the mount method, which takes two parameters as input:
4. Finally, we need to launch our rocket for it to serve requests. There are two mechanisms to launch a Rocket instance, but we will focus on the preferred approach for this tutorial's purposes: the launch attribute. Add it above the rocket function defined in the previous step:
#[launch]
fn rocket() -> Rocket {
rocket::ignite().mount("/", routes![root, static_files])
}
5. At the root of our project, create a new folder named site and inside of it, create a folder named static.
6. Inside the static folder, create anindex.html file with the following content:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Late Night Confessions</title>
<link href="https://fonts.googleapis.com/css2?family=Lato:ital,wght@0,300;1,400&display=swap" rel="stylesheet" />
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/cirrus-ui@0.6.0/dist/cirrus.min.css" />
<link rel="stylesheet" href="/static/styles.css" />
</head>
<body>
<h1>Fly 🚀 me to the moon 🌙</h1>
</body>
</html>
7. Create an empty styles.css file in the same folder. We will populate it later on.
Go ahead and cargo run the project and then browse to localhost:8000. Congratulations, you have a working Rocket server!
Look ma! I can serve a page! 👶
With our server up and running, we are ready to take on the next task and pour some Diesel (pun intended) into our Rocket to gain database support!