blog bg

April 09, 2024

Building a Movies REST API with Rust: A Beginner's Guide

Share what you learn in this blog to prepare for your interview, create your forever-free profile now, and explore how to monetize your valuable knowledge.

 

In the vast landscape of software development, building a REST API to serve movie data is a common task that many developers undertake. Rust, with its growing popularity and powerful features, presents an excellent choice for such endeavors. In this guide, we'll walk through the process of building a Movies REST API using Rust, exploring key concepts and techniques along the way.

 

Why Rust?
Rust has gained traction in recent years for its focus on performance, memory safety, and concurrency. These features make it an ideal candidate for building high-performance web services. Additionally, Rust's strong type system and expressive syntax promote clean and maintainable code, crucial for long-term project viability.

 

Setting Up the Project
To begin our journey, let's set up a new Rust project using Cargo, Rust's package manager and build system. Open your terminal and execute the following commands:

 

>> cargo new movies-api
>> cd movies-api

 

This creates a new Rust project named movies-api. Next, let's add the necessary dependencies to our Cargo.toml file:

 

[dependencies]
actix-web = "4.0"
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"

 

We're using Actix-Web, a powerful and ergonomic web framework for Rust, along with Serde, a popular library for serializing and deserializing data.

 

Implementing the Movies API

Now that our project is set up, let's implement the Movies REST API. We'll start by defining our movie data model:

// src/model.rs

use serde::{Deserialize, Serialize};

#[derive(Debug, Serialize, Deserialize)]
pub struct Movie {
    pub id: u64,
    pub title: String,
    pub director: String,
    pub year: u32,
}

 

Next, we'll create our API routes and handlers:

 

// src/main.rs

use actix_web::{web, App, HttpServer, Responder, HttpResponse};
use serde::{Deserialize, Serialize};
use serde_json::json;
use std::sync::Mutex;

type Db = Mutex<Vec<model::Movie>>;

async fn get_movies(db: web::Data<Db>) -> impl Responder {
    let movies = db.lock().unwrap();
    web::Json(json!(movies.clone()))
}

async fn get_movie_by_id(path: web::Path<u64>, db: web::Data<Db>) -> impl Responder {
    let movies = db.lock().unwrap();
    for movie in &*movies {
        if movie.id == *path {
            return web::Json(json!(movie));
        }
    }
    HttpResponse::NotFound().body("Movie not found")
}

async fn update_movie(
    path: web::Path<u64>,
    movie: web::Json<model::Movie>,
    db: web::Data<Db>,
) -> impl Responder {
    let mut movies = db.lock().unwrap();
    for m in &mut *movies {
        if m.id == *path {
            *m = movie.into_inner();
            return HttpResponse::Ok().finish();
        }
    }
    HttpResponse::NotFound().body("Movie not found")
}

async fn delete_movie(path: web::Path<u64>, db: web::Data<Db>) -> impl Responder {
    let mut movies = db.lock().unwrap();
    let index = movies.iter().position(|m| m.id == *path);
    match index {
        Some(i) => {
            movies.remove(i);
            HttpResponse::NoContent().finish()
        }
        None => HttpResponse::NotFound().body("Movie not found"),
    }
}

#[actix_web::main]
async fn main() -> std::io::Result<()> {
    // Initialize our in-memory database
    let db: Db = Mutex::new(vec![
        model::Movie {
            id: 1,
            title: String::from("Inception"),
            director: String::from("Christopher Nolan"),
            year: 2010,
        },
        model::Movie {
            id: 2,
            title: String::from("The Shawshank Redemption"),
            director: String::from("Frank Darabont"),
            year: 1994,
        },
    ]);

    HttpServer::new(move || {
        App::new()
            .data(db.clone())
            .route("/movies", web::get().to(get_movies))
            .route("/movies/{id}", web::get().to(get_movie_by_id))
            .route("/movies", web::post().to(add_movie))
            .route("/movies/{id}", web::put().to(update_movie))
            .route("/movies/{id}", web::delete().to(delete_movie))
    })
    .bind("127.0.0.1:8080")?
    .run()
    .await
}

 

 

In this code, we have defined the following routes:
GET /movies: Retrieves all movies.

  • GET /movies/{id}: Retrieves a specific movie by its ID.
  • POST /movies: Adds a new movie to the database.
  • PUT /movies/{id}: Updates an existing movie.
  • DELETE /movies/{id}: Deletes a movie by its ID.

 

Running the API

To run our API, execute the following command in your terminal:

 

>> cargo run

 

This will start the web server, and you should see output indicating that it's listening for incoming connections. Now you can access the API at http://localhost:8080/movies.

 

Conclusion

In this guide, we've explored how to build a Movies REST API using Rust and Actix-Web. We covered setting up a new Rust project, defining data models, implementing API routes, and running the server. This is just a starting point; you can expand this API by adding more endpoints, integrating with a database, implementing authentication, and more. Rust's robust ecosystem and performance make it an excellent choice for building scalable and efficient web services. Enjoy coding!

231 views

Please Login to create a Question