Build a REST API with Rust and MongoDB - Actix web Version
Tags: rust, mongodb, actixweb, restapi
Published: 2022-05-26 12:32:38

---
title: Build a REST API with Rust and MongoDB - Actix web Version
published: true
description: This post will discuss building a user management application with Rust using the Actix web framework and MongoDB
tags: #rust #mongoDB #actixweb #restapi
cover_image: https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ak4lofdd8r80jz606npv.png
---
REST API has become the De Facto for connecting and transferring data from one source to another. It offers a set of guidelines and architectural patterns for designing and developing web services.

This post will discuss building a user management application with Rust using the [Actix web](https://actix.rs/) framework and [MongoDB](https://www.mongodb.com/). At the end of this tutorial, we will learn how to structure a Rust application, build a REST API and persist our data using MongoDB.

Actix web is an HTTP web framework written in Rust with performance and productivity support. Actix web ships with features like type-safety, reusability, logging, static file serving and much more that developers can leverage to build scalable applications.

MongoDB is a document-based database management program used as an alternative to relational databases. MongoDB supports working with large sets of distributed data with options to store or retrieve information seamlessly.

The complete source code is available in this [repository](https://github.com/Mr-Malomz/actix-mongo-api).

# Prerequisites

To fully grasp the concepts presented in this tutorial, experience with Rust is required. Experience with MongoDB isn’t a requirement, but it’s nice to have.

We will also be needing the following:

- A [MongoDB account](https://www.mongodb.com/) to host database. [**Signup**](https://www.mongodb.com/cloud/atlas/register) **is completely free**.
- [Postman](https://www.postman.com/downloads/) or any API testing application

# Let’s code
## Getting Started

To get started, we need to navigate to the desired directory and run the command below in our terminal 

```bash
    cargo new actix-mongo-api && cd actix-mongo-api
```
This command creates a Rust project called  `actix-mongo-api` and navigates into the project directory.

Next, we proceed to install the required dependencies by modifying the `[dependencies]` section of the `Cargo.toml` file as shown below:
```toml
    //other code section goes here
    
    [dependencies]
    actix-web = "4"
    serde = "1.0.136"
    dotenv = "0.15.0"
    futures = "0.3"
    
    [dependencies.mongodb]
    version = "2.2.0"
    default-features = false
    features = ["async-std-runtime"] 
```
`actix-web =` `"``4``"` is a Rust-based framework for building web applications.

`serde = "1.0.136"` is a framework for serializing and deserializing Rust data structures. E.g. convert Rust structs to JSON.

`dotenv = "0.15.0"` is a library for managing environment variables.

`futures = "0.3"` is a library for doing asynchronous programming in rust

`[dependencies.mongodb]` is a driver for connecting to MongoDB. It also specifies the required version and the feature type(Asynchronous API).

We need to run the command below to install the dependencies:
```bash
    cargo build
```
## Application Entry Point

With the project dependencies installed, modify the `main.rs` file in the `src` folder to the following:
```rust
    use actix_web::{get, App, HttpResponse, HttpServer, Responder};
    
    #[get("/")]
    async fn hello() -> impl Responder {
        HttpResponse::Ok().json("Hello from rust and mongoDB")
    }
    
    #[actix_web::main]
    async fn main() -> std::io::Result<()> {
        HttpServer::new(|| App::new().service(hello))
            .bind(("localhost", 8080))?
            .run()
            .await
    }
```
The snippet above does the following:

- Imports the required dependencies
- Creates a `hello` handler that uses the Rust macro to specify the HTTP method, the route path `/`, and returns a JSON of `Hello from rust and mongoDB`.
- Uses the `#[actix_web::main]` macro to run the `main` function asynchronously within the actix runtime. The `main` function does the following:
    - Creates a new server using `HttpServer` struct that uses a closure to serve incoming requests using the `App` instance. The `App` also registers the `hello` handler. `HttpServer` is the backbone of our application; it takes care of request handling, the maximum number of connections allowed, layered security, e.t.c, while `App` handles application logic like request handlers, middlewares, routing, e.t.c.
    - Configures the server to run asynchronously and process HTTP requests on `localhost:8080`.

Next, we can test our application by running the command below in our terminal.
```bash
    cargo run
```

![Testing the app](https://paper-attachments.dropbox.com/s_F59F3317E88EE92046EF41C74313DA1EE5A1440BFF5C32BE319073C080AE6029_1652485368076_Screenshot+2022-05-14+at+00.39.19.png)

## Module system in Rust

A module in Rust is a mechanism for splitting code into reusable components and managing visibility between them. Modules help us maintain a good project structure for our project.

To do this, we need to navigate to the `src` folder and create `api`, `models`, and `repository` folder with the corresponding `mod.rs`  file to manage visibility.

![Updated project folder structure](https://paper-attachments.dropbox.com/s_F59F3317E88EE92046EF41C74313DA1EE5A1440BFF5C32BE319073C080AE6029_1652487020091_Screenshot+2022-05-14+at+01.10.01.png)


`api` is for modularizing API handlers.

`models` is for modularizing data logics.

`repository` is for modularizing database logics.

**Adding reference to the Modules**
To use the code in the modules, we need to declare them as a module and import them into the `main.rs` file.
```rust
    //add the modules
    mod api; 
    mod models;
    mod repository;
    
    use actix_web::{get, App, HttpResponse, HttpServer, Responder};
    
    // the remaining part of our code goes here
```
## Setting up MongoDB

With that done, we need to log in or sign up into our [MongoDB](http://) account. Click the project dropdown menu and click on the **New Project** button.

![New Project](https://paper-attachments.dropbox.com/s_B58C50BB61437E91E681B5825981F54733906EC9A6070DFB4A21109C66F0E3FA_1637365380025_Screenshot+2021-11-20+at+00.33.27.png)


Enter the `rust-api` as the project name, click **Next**, and click **Create Project..**

![enter project name](https://paper-attachments.dropbox.com/s_F59F3317E88EE92046EF41C74313DA1EE5A1440BFF5C32BE319073C080AE6029_1652487876357_Screenshot+2022-05-14+at+01.24.06.png)
![Create Project](https://paper-attachments.dropbox.com/s_0E637C74C0D58C5FC3FBA1D21E2CF4F11E7592B7929DAE10790A9D12FE972D1D_1640511449329_Screenshot+2021-12-26+at+10.36.27.png)


Click on **Build a Database**

![](https://paper-attachments.dropbox.com/s_F59F3317E88EE92046EF41C74313DA1EE5A1440BFF5C32BE319073C080AE6029_1652487996260_Screenshot+2022-05-14+at+01.26.08.png)


Select **Shared** as the type of database.

![Shared highlighted in red](https://paper-attachments.dropbox.com/s_B58C50BB61437E91E681B5825981F54733906EC9A6070DFB4A21109C66F0E3FA_1637334670330_Screenshot+2021-11-19+at+15.59.38.png)


Click on **Create** to setup a cluster. This might take sometime to setup.

![Creating a cluster](https://paper-attachments.dropbox.com/s_B58C50BB61437E91E681B5825981F54733906EC9A6070DFB4A21109C66F0E3FA_1637334961608_Screenshot+2021-11-19+at+16.13.28.png)


Next, we need to create a user to access the database externally by inputting the **Username**, **Password** and then clicking on **Create User**. We also need to add our IP address to safely connect to the database by clicking on the **Add My Current IP Address** button. Then click on **Finish and Close** to save changes.

![Create user](https://paper-attachments.dropbox.com/s_F59F3317E88EE92046EF41C74313DA1EE5A1440BFF5C32BE319073C080AE6029_1652488168829_Screenshot+2022-05-14+at+01.27.52.png)
![Add IP](https://paper-attachments.dropbox.com/s_F59F3317E88EE92046EF41C74313DA1EE5A1440BFF5C32BE319073C080AE6029_1652488174631_Screenshot+2022-05-14+at+01.28.39.png)


On saving the changes, we should see a Database Deployments screen, as shown below:

![Database Screen](https://paper-attachments.dropbox.com/s_F59F3317E88EE92046EF41C74313DA1EE5A1440BFF5C32BE319073C080AE6029_1652488234460_Screenshot+2022-05-14+at+01.30.25.png)

## Connecting our application to MongoDB

With the configuration done, we need to connect our application with the database created. To do this, click on the **Connect** button


![Connect to database](https://paper-attachments.dropbox.com/s_0E637C74C0D58C5FC3FBA1D21E2CF4F11E7592B7929DAE10790A9D12FE972D1D_1640512936968_Screenshot+2021-12-26+at+10.51.34.png)


Click on **Connect your application**, change the **Driver** to `Rust` and the **Version** as shown below. Then click on the copy **icon** to copy the connection string.

![connect application](https://paper-attachments.dropbox.com/s_0E637C74C0D58C5FC3FBA1D21E2CF4F11E7592B7929DAE10790A9D12FE972D1D_1640513302462_Screenshot+2021-12-26+at+11.03.40.png)
![Copy connection string](https://paper-attachments.dropbox.com/s_F59F3317E88EE92046EF41C74313DA1EE5A1440BFF5C32BE319073C080AE6029_1652914102292_Screenshot+2022-05-18+at+23.46.33.png)


**Setup Environment Variable**
Next, we must modify the copied connection string with the user's password we created earlier and change the database name. To do this, first, we need to create a `.env` file in the root directory, and in this file, add the snippet copied:

```md
    MONGOURI=mongodb+srv://<YOUR USERNAME HERE>:<YOUR PASSWORD HERE>@cluster0.e5akf.mongodb.net/myFirstDatabese?retryWrites=true&w=majority
```
Sample of a properly filled connection string below:

```md  
MONGOURI=mongodb+srv://malomz:malomzPassword@cluster0.e5akf.mongodb.net/golangDB?retryWrites=true&w=majority
```

## Creating REST APIs

With the setup done, we need to create a model to represent our application data. To do this, we need to navigate to the `models` folder, and in this folder, create a `user_model.rs` file and add the snippet below:

```rust
    use mongodb::bson::oid::ObjectId;
    use serde::{Serialize, Deserialize};
    
    #[derive(Debug, Serialize, Deserialize)]
    pub struct User {
        #[serde(rename = "_id", skip_serializing_if = "Option::is_none")]
        pub id: Option<ObjectId>,
        pub name: String,
        pub location: String,
        pub title: String,
    }
```
The snippet above does the following:

- Imports the required dependencies
- Uses the `derive` macro to generate implementation support for formatting the output, serializing, and deserializing the data structure.
- Creates a  `User` struct with required properties. We also added field attributes to the `id` property to rename and ignore the field if it is empty.

**PS**: *The `pub` modifier makes the struct and its property public and can be accessed from other files/modules.*

Next, we must register the `user_model.rs` file as part of the `models` module. To do this, open the `mod.rs` in the `models` folder and add the snippet below:
```rust
    pub mod user_model;
```
**Create a User Endpoint**
With the model fully set up and made available to be consumed, we can now create our database logic to create a user. To do this, First, we need to navigate to the `repository` folder, and in this folder, create a `mongodb_repo.rs` file and add the snippet below:
```rust
    use std::env;
    extern crate dotenv;
    use dotenv::dotenv;
    
    use mongodb::{
        bson::{extjson::de::Error},
        results::{ InsertOneResult},
        Client, Collection,
    };
    use crate::models::user_model::User;
    
    pub struct MongoRepo {
        col: Collection<User>,
    }
    
    impl MongoRepo {
        pub async fn init() -> Self {
            dotenv().ok();
            let uri = match env::var("MONGOURI") {
                Ok(v) => v.to_string(),
                Err(_) => format!("Error loading env variable"),
            };
            let client = Client::with_uri_str(uri).unwrap();
            let db = client.database("rustDB");
            let col: Collection<User> = db.collection("User");
            MongoRepo { col }
        }
    
        pub async fn create_user(&self, new_user: User) -> Result<InsertOneResult, Error> {
            let new_doc = User {
                id: None,
                name: new_user.name,
                location: new_user.location,
                title: new_user.title,
            };
            let user = self
                .col
                .insert_one(new_doc, None)
                .await
                .ok()
                .expect("Error creating user");
            Ok(user)
        }
    }
```
The snippet above does the following:

- Imports the required dependencies
- Creates a `MongoRepo` struct with a `col` field to access MongoDB collection
- Creates an implementation block that adds methods to the `MongoRepo` struct
- Adds an `init` method to the implementation block to load the environment variable, creates a connection to the database, and returns an instance of the `MongoRepo` struct
- Adds a `create_user` method that takes in a `self` and `new_user` as parameters and returns the created user or an error. Inside the method, we created a new document using the `User` struct. Then we use the `self` referencing the `MongoRepo` struct to access the `insert_one` function from the collection to create a new user and handle errors. Finally, we returned the created user information.

**PS:** *The `None` specified when creating a new document tells MongoDB to automatically generate the user’s* ***id****.* 

Next, we must register the `mongodb_repo.rs` file as part of the `repository` module. To do this, open the `mod.rs` in the `repository` folder and add the snippet below:
```rust
    pub mod mongodb_repos;
```
Secondly, we need to create a handler that uses the `create_user` method from the `repository` to create a user. To do this, we need to navigate to the `api` folder, and in this folder, create a `user_api.rs` file and add the snippet below:
```rust
    use crate::{models::user_model::User, repository::mongodb_repo::MongoRepo};
    use actix_web::{
        post,
        web::{Data, Json},
        HttpResponse,
    };
    
    #[post("/user")]
    pub async fn create_user(db: Data<MongoRepo>, new_user: Json<User>) -> HttpResponse {
        let data = User {
            id: None,
            name: new_user.name.to_owned(),
            location: new_user.location.to_owned(),
            title: new_user.title.to_owned(),
        };
        let user_detail = db.create_user(data).await;
        match user_detail {
            Ok(user) => HttpResponse::Ok().json(user),
            Err(err) => HttpResponse::InternalServerError().body(err.to_string()),
        }
    }
```
The snippet above does the following:

- Imports the required dependencies
- Uses the routing macro to specify HTTP method and corresponding route
- Creates a `create_user` handler that takes in the `db`, a type to the `MongoRepo` and a `new_user` as parameters. Inside the handler, we created a `data` variable for creating a user, inserted it into the database using the `db.create_user` method, and returned the correct response if the insert was successful or error if any.

**PS:** *The `Data`  and `Json` struct used in defining the parameter is for managing application state shared across routes and extracting JSON data from request payloads, respectively.*

Finally, we need to modify our application entry point to include the `create_user` handler. To do this, we need to navigate to the `main.rs` file and modify it as shown below:
```rust
    mod api;
    mod models;
    mod repository;
    
    //modify imports below
    use actix_web::{web::Data, App, HttpServer};
    use api::user_api::{create_user};
    use repository::mongodb_repo::MongoRepo;
    
    #[actix_web::main]
    async fn main() -> std::io::Result<()> {
        let db = MongoRepo::init().await;
        let db_data = Data::new(db);
        HttpServer::new(move || {
            App::new()
                .app_data(db_data.clone())
                .service(create_user)
        })
        .bind(("127.0.0.1", 8080))?
        .run()
        .await
    }
```
The snippet above does the following:

- Imports the required dependencies
- Creates a `db` variable to establish a connection to MongoDB by calling the `init()` method and adds it to a new instance of the `Data` struct so that the database state can be available across the application scope.
- Uses the `app_data` and `service`  function to add the application data and the handler to the `App` instance.

**PS:** _The `move` keyword attached to the closure gives it ownership of the MongoDB configuration._

**Get a User Endpoint**
To get the details of a user, we must first modify the `mongodb_repo.rs` file by adding a `get_user` method to the implementation block.
```rust
    use std::env;
    extern crate dotenv;
    use dotenv::dotenv;
    
    use mongodb::{
        bson::{extjson::de::Error, oid::ObjectId, doc}, //modify here
        results::{ InsertOneResult},
        Client, Collection,
    };
    use crate::models::user_model::User;
    
    pub struct MongoRepo {
        col: Collection<User>,
    }
    
    impl MongoRepo {
        pub async fn init() -> Self {
            //init code goes here
        }
    
        pub async fn create_user(&self, new_user: User) -> Result<InsertOneResult, Error> {
            //create_user code goes here
        }
    
        pub async fn get_user(&self, id: &String) -> Result<User, Error> {
            let obj_id = ObjectId::parse_str(id).unwrap();
            let filter = doc! {"_id": obj_id};
            let user_detail = self
                .col
                .find_one(filter, None)
                .await
                .ok()
                .expect("Error getting user's detail");
            Ok(user_detail.unwrap())
        }
    }
```
The snippet above does the following:

- Modifies the dependencies to include `oid::ObjectId` and `doc`
- Adds a `get_user` method that takes in a `self` and `id` as parameters and returns the user detail or an error. Inside the method, we converted the `id` to an `ObjectId`  and used it as a filter to get matching document. Then we use the `self` referencing the `MongoRepo` struct to access the `find_one` function from the collection to get the details of the user and handle errors. Finally, we returned the created user information.


Secondly, we need to modify `user_api.rs` by creating a handler that uses the `get_user` method from the `repository` to get a user. 
```rust
    use crate::{models::user_model::User, repository::mongodb_repo::MongoRepo};
    use actix_web::{
        post, get, //modify here
        web::{Data, Json, Path}, //modify here
        HttpResponse,
    };
    
    #[post("/user")]
    pub async fn create_user(db: Data<MongoRepo>, new_user: Json<User>) -> HttpResponse {
        //create_user code goes here
    }
    
    #[get("/user/{id}")]
    pub async fn get_user(db: Data<MongoRepo>, path: Path<String>) -> HttpResponse {
        let id = path.into_inner();
        if id.is_empty() {
            return HttpResponse::BadRequest().body("invalid ID");
        }
        let user_detail = db.get_user(&id).await;
        match user_detail {
            Ok(user) => HttpResponse::Ok().json(user),
            Err(err) => HttpResponse::InternalServerError().body(err.to_string()),
        }
    }
```
The snippet above does the following:

- Modifies the dependencies to include `get` and `Path`
- Uses the routing macro to specify HTTP method, corresponding route and route parameter
- Creates a `get_user` handler that takes in the `db`, a type to the `MongoRepo` and a `path` for accessing route path as parameters. Inside the handler, we created an `id` variable to get the user’s **id**, get the user’s details from the database using the `db.get_user` method. We returned the correct response if the request was successful or error if any.

Finally, we need to modify our application entry point(`main.rs`)to include the `get_user` handler by importing the handler and adding a new service for it.
```rust
    mod api;
    mod models;
    mod repository;
    
    //modify imports below
    use actix_web::{web::Data, App, HttpServer};
    use api::user_api::{create_user, get_user}; //import the handler here
    use repository::mongodb_repo::MongoRepo;
    
    #[actix_web::main]
    async fn main() -> std::io::Result<()> {
        let db = MongoRepo::init().await;
        let db_data = Data::new(db);
        HttpServer::new(move || {
            App::new()
                .app_data(db_data.clone())
                .service(create_user)
                .service(get_user) //add this
        })
        .bind(("127.0.0.1", 8080))?
        .run()
        .await
    }
```

**Edit a User Endpoint**
To edit a user, we must first modify the `mongodb_repo.rs` file by adding an `edit_user` method to the implementation block.
```rust
    use std::env;
    extern crate dotenv;
    use dotenv::dotenv;
    
    use mongodb::{
        bson::{extjson::de::Error, oid::ObjectId, doc}, 
        results::{ InsertOneResult, UpdateResult}, //modify here
        Client, Collection,
    };
    use crate::models::user_model::User;
    
    pub struct MongoRepo {
        col: Collection<User>,
    }
    
    impl MongoRepo {
        pub async fn init() -> Self {
            //init code goes here
        }
    
        pub async fn create_user(&self, new_user: User) -> Result<InsertOneResult, Error> {
            //create_user code goes here
        }
    
        pub async fn get_user(&self, id: &String) -> Result<User, Error> {
            //get_user code goes here
        }
    
        pub async fn update_user(&self, id: &String, new_user: User) -> Result<UpdateResult, Error> {
            let obj_id = ObjectId::parse_str(id).unwrap();
            let filter = doc! {"_id": obj_id};
            let new_doc = doc! {
                "$set":
                    {
                        "id": new_user.id,
                        "name": new_user.name,
                        "location": new_user.location,
                        "title": new_user.title
                    },
            };
            let updated_doc = self
                .col
                .update_one(filter, new_doc, None)
                .await
                .ok()
                .expect("Error updating user");
            Ok(updated_doc)
        }
    }
```
The snippet above does the following:

- Modifies the dependencies to include `UpdateResult`
- Adds an `update_user` method that takes in a  `self`, `id`, and `new_user` parameters and returns the updated user detail or an error. Inside the method, we converted the `id` to an `ObjectId`, created a `filter`  variable to get the matching document we wanted to update and used the `doc` macro to update the document fields. Then we use the `self` referencing the `MongoRepo` struct to access the `update_one` function from the collection to update the user matching the `filter` specified and handle errors. Finally, we returned the updated user information.

Secondly, we need to modify `user_api.rs` by creating a handler that uses the `update_user` method from the `repository` to update a user. 
```rust
    use crate::{models::user_model::User, repository::mongodb_repo::MongoRepo};
    use actix_web::{
        post, get, put, //modify here
        web::{Data, Json, Path},
        HttpResponse,
    };
    use mongodb::bson::oid::ObjectId; //add this
    
    #[post("/user")]
    pub async fn create_user(db: Data<MongoRepo>, new_user: Json<User>) -> HttpResponse {
        //create_user code goes here
    }
    
    #[get("/user/{id}")]
    pub async fn get_user(db: Data<MongoRepo>, path: Path<String>) -> HttpResponse {
        //get_user code goes here
    }
    
    #[put("/user/{id}")]
    pub async fn update_user(
        db: Data<MongoRepo>,
        path: Path<String>,
        new_user: Json<User>,
    ) -> HttpResponse {
        let id = path.into_inner();
        if id.is_empty() {
            return HttpResponse::BadRequest().body("invalid ID");
        };
        let data = User {
            id: Some(ObjectId::parse_str(&id).unwrap()),
            name: new_user.name.to_owned(),
            location: new_user.location.to_owned(),
            title: new_user.title.to_owned(),
        };
        let update_result = db.update_user(&id, data).await;
        match update_result {
            Ok(update) => {
                if update.matched_count == 1 {
                    let updated_user_info = db.get_user(&id).await;
                    return match updated_user_info {
                        Ok(user) => HttpResponse::Ok().json(user),
                        Err(err) => HttpResponse::InternalServerError().body(err.to_string()),
                    };
                } else {
                    return HttpResponse::NotFound().body("No user found with specified ID");
                }
            }
            Err(err) => HttpResponse::InternalServerError().body(err.to_string()),
        }
    }
```
The snippet above does the following:

- Modifies the dependencies to include `put` and `ObjectId`
- Uses the routing macro to specify HTTP method, corresponding route and route parameter
- Creates an `update_user` handler that takes in the `db`, a type to the `MongoRepo`, `path`, and `new_user` as parameters. Inside the handler, we created an `id` variable to get the user’s **id**, update the user’s details from the database using the `db.update_user` method by passing in the updated user’s information. Finally, we checked if the update was successful and returned the updated user or error if any.

Finally, we need to modify our application entry point(`main.rs`)to include the `update_user` handler by importing the handler and adding a new service for it.
```rust
    mod api;
    mod models;
    mod repository;
    
    //modify imports below
    use actix_web::{web::Data, App, HttpServer};
    use api::user_api::{create_user, get_user, update_user}; //import the handler here
    use repository::mongodb_repo::MongoRepo;
    
    #[actix_web::main]
    async fn main() -> std::io::Result<()> {
        let db = MongoRepo::init().await;
        let db_data = Data::new(db);
        HttpServer::new(move || {
            App::new()
                .app_data(db_data.clone())
                .service(create_user)
                .service(get_user)
                .service(update_user) //add this
        })
        .bind(("127.0.0.1", 8080))?
        .run()
        .await
    }

```
**Delete a User Endpoint**
To delete a user, we must first modify the `mongodb_repo.rs` file by adding an `delete_user` method to the implementation block.
```rust
    use std::env;
    extern crate dotenv;
    use dotenv::dotenv;
    
    use mongodb::{
        bson::{extjson::de::Error, oid::ObjectId, doc}, 
        results::{ InsertOneResult, UpdateResult, DeleteResult}, //modify here
        Client, Collection,
    };
    use crate::models::user_model::User;
    
    pub struct MongoRepo {
        col: Collection<User>,
    }
    
    impl MongoRepo {
        pub async fn init() -> Self {
            //init code goes here
        }
    
        pub async fn create_user(&self, new_user: User) -> Result<InsertOneResult, Error> {
            //create_user code goes here
        }
    
        pub async fn get_user(&self, id: &String) -> Result<User, Error> {
            //get_user code goes here
        }
    
        pub async fn update_user(&self, id: &String, new_user: User) -> Result<UpdateResult, Error> {
            //update_user code goes here
        }
    
        pub async fn delete_user(&self, id: &String) -> Result<DeleteResult, Error> {
            let obj_id = ObjectId::parse_str(id).unwrap();
            let filter = doc! {"_id": obj_id};
            let user_detail = self
                .col
                .delete_one(filter, None)
                .await
                .ok()
                .expect("Error deleting user");
            Ok(user_detail)
        }
    }
```
The snippet above does the following:

- Modifies the dependencies to include `DeleteResult`
- Adds a `delete_user` method that takes in a  `self` and `id` as parameters and returns the deleted user detail or an error. Inside the method, we converted the `id` to an `ObjectId` and created a `filter`  variable to get the matching document we wanted to delete. Then we use the `self` referencing the `MongoRepo` struct to access the `delete_one` function from the collection to delete the user matching the `filter` specified and handle errors. Finally, we returned the deleted user information.

Secondly, we need to modify `user_api.rs` by creating a handler that uses the `delete_user` method from the `repository` to delete a user. 
```rust
    use crate::{models::user_model::User, repository::mongodb_repo::MongoRepo};
    use actix_web::{
        post, get, put, delete, //modify here
        web::{Data, Json, Path},
        HttpResponse,
    };
    use mongodb::bson::oid::ObjectId; //add this
    
    #[post("/user")]
    pub async fn create_user(db: Data<MongoRepo>, new_user: Json<User>) -> HttpResponse {
        //create_user code goes here
    }
    
    #[get("/user/{id}")]
    pub async fn get_user(db: Data<MongoRepo>, path: Path<String>) -> HttpResponse {
        //get_user code goes here
    }
    
    #[put("/user/{id}")]
    pub async fn update_user(
        db: Data<MongoRepo>,
        path: Path<String>,
        new_user: Json<User>,
    ) -> HttpResponse {
        //update_user code goes here
    }
    
    #[delete("/user/{id}")]
    pub async fn delete_user(db: Data<MongoRepo>, path: Path<String>) -> HttpResponse {
        let id = path.into_inner();
        if id.is_empty() {
            return HttpResponse::BadRequest().body("invalid ID");
        };
        let result = db.delete_user(&id).await;
        match result {
            Ok(res) => {
                if res.deleted_count == 1 {
                    return HttpResponse::Ok().json("User successfully deleted!");
                } else {
                    return HttpResponse::NotFound().json("User with specified ID not found!");
                }
            }
            Err(err) => HttpResponse::InternalServerError().body(err.to_string()),
        }
    }
```
The snippet above does the following:

- Modifies the dependencies to include `delete`
- Uses the routing macro to specify HTTP method, corresponding route and route parameter
- Creates a `delete_user` handler that takes in the `db`, a type to the `MongoRepo` and `path` as parameters. Inside the handler, we created an `id` variable to get the user’s **id** and ****delete the user from the database using the `db.delete_user` method by passing in the `id`. Finally, we returned the appropriate response or error if any.

Finally, we need to modify our application entry point(`main.rs`)to include the `delete_user` handler by importing the handler and adding a new service for it.
```rust
    mod api;
    mod models;
    mod repository;
    
    //modify imports below
    use actix_web::{web::Data, App, HttpServer};
    use api::user_api::{create_user, get_user, update_user, delete_user}; //import the handler here
    use repository::mongodb_repo::MongoRepo;
    
    #[actix_web::main]
    async fn main() -> std::io::Result<()> {
        let db = MongoRepo::init().await;
        let db_data = Data::new(db);
        HttpServer::new(move || {
            App::new()
                .app_data(db_data.clone())
                .service(create_user)
                .service(get_user)
                .service(update_user) 
                .service(delete_user) //add this
        })
        .bind(("127.0.0.1", 8080))?
        .run()
        .await
    }
```

**Get all Users Endpoint**
To get the list of users, we must first modify the `mongodb_repo.rs` file by adding a `get_all_users` method to the implementation block.
```rust
    use std::env;
    extern crate dotenv;
    use dotenv::dotenv;
    
    use mongodb::{
        bson::{extjson::de::Error, oid::ObjectId, doc}, 
        results::{ InsertOneResult, UpdateResult, DeleteResult},
        Client, Collection,
    };
    use futures::stream::TryStreamExt; //add this
    use crate::models::user_model::User;
    
    pub struct MongoRepo {
        col: Collection<User>,
    }
    
    impl MongoRepo {
        pub async fn init() -> Self {
            //init code goes here
        }
    
        pub async fn create_user(&self, new_user: User) -> Result<InsertOneResult, Error> {
            //create_user code goes here
        }
    
        pub async fn get_user(&self, id: &String) -> Result<User, Error> {
            //get_user code goes here
        }
    
        pub async fn update_user(&self, id: &String, new_user: User) -> Result<UpdateResult, Error> {
            //update_user code goes here
        }
    
        pub async fn delete_user(&self, id: &String) -> Result<DeleteResult, Error> {
            //delete_user code goes here
        }
        
        pub async fn get_all_users(&self) -> Result<Vec<User>, Error> {
        let mut cursors = self
            .col
            .find(None, None)
            .await
            .ok()
            .expect("Error getting list of users");
        let mut users: Vec<User> = Vec::new();
        while let Some(user) = cursors
            .try_next()
            .await
            .ok()
            .expect("Error mapping through cursor")
        {
            users.push(user)
        }
        Ok(users)
        }
    }
```
The snippet above adds a `get_all_users` method that takes in a  `self` as a parameter and returns the list of users or an error. Inside the method,  we use the `self` referencing the `MongoRepo` struct to access the `find` function from the collection without any filter so that it can match all the documents inside the database, returned the list optimally using the `try_next()` method to loop through the list of users, and handle errors.

Secondly, we need to modify `user_api.rs` by creating a handler that uses the `get_all_users` method from the `repository` to get list of users. 
```rust
    use crate::{models::user_model::User, repository::mongodb_repo::MongoRepo};
    use actix_web::{
        post, get, put, delete, 
        web::{Data, Json, Path},
        HttpResponse,
    };
    use mongodb::bson::oid::ObjectId; 
    
    #[post("/user")]
    pub async fn create_user(db: Data<MongoRepo>, new_user: Json<User>) -> HttpResponse {
        //create_user code goes here
    }
    
    #[get("/user/{id}")]
    pub async fn get_user(db: Data<MongoRepo>, path: Path<String>) -> HttpResponse {
        //get_user code goes here
    }
    
    #[put("/user/{id}")]
    pub async fn update_user(
        db: Data<MongoRepo>,
        path: Path<String>,
        new_user: Json<User>,
    ) -> HttpResponse {
        //update_user code goes here
    }
    
    #[delete("/user/{id}")]
    pub async fn delete_user(db: Data<MongoRepo>, path: Path<String>) -> HttpResponse {
        //delet_user code goes here
    }
    
    #[get("/users")]
    pub async fn get_all_users(db: Data<MongoRepo>) -> HttpResponse {
        let users = db.get_all_users().await;
        match users {
            Ok(users) => HttpResponse::Ok().json(users),
            Err(err) => HttpResponse::InternalServerError().body(err.to_string()),
        }
    }
```
The snippet above does the following:

- Uses the routing macro to specify HTTP method and corresponding route
- Creates a `get_all_users` handler that uses the `db.delete_user` method to get the list of users. Then, we returned the list of users or error if any.

Finally, we need to modify our application entry point(`main.rs`)to include the `get_all_users` handler by importing the handler and adding a new service for it.
```rust
    mod api;
    mod models;
    mod repository;
    
    //modify imports below
    use actix_web::{web::Data, App, HttpServer};
    use api::user_api::{create_user, get_user, update_user, delete_user, get_all_users}; //import the handler here
    use repository::mongodb_repo::MongoRepo;
    
    #[actix_web::main]
    async fn main() -> std::io::Result<()> {
        let db = MongoRepo::init().await;
        let db_data = Data::new(db);
        HttpServer::new(move || {
            App::new()
                .app_data(db_data.clone())
                .service(create_user)
                .service(get_user)
                .service(update_user) 
                .service(delete_user) 
               .service(get_all_users)//add this
        })
        .bind(("127.0.0.1", 8080))?
        .run()
        .await
    }

```
With that done, we can test our application by running the command below in our terminal.
```bash
    cargo run
```


![Create a user endpoint](https://paper-attachments.dropbox.com/s_F59F3317E88EE92046EF41C74313DA1EE5A1440BFF5C32BE319073C080AE6029_1653085524444_Screenshot+2022-05-20+at+23.15.21.png)



![Get a user endpoint](https://paper-attachments.dropbox.com/s_F59F3317E88EE92046EF41C74313DA1EE5A1440BFF5C32BE319073C080AE6029_1653085567350_Screenshot+2022-05-20+at+23.16.06.png)

![Edit a user endpoint](https://paper-attachments.dropbox.com/s_F59F3317E88EE92046EF41C74313DA1EE5A1440BFF5C32BE319073C080AE6029_1653085601694_Screenshot+2022-05-20+at+23.17.19.png)

![Delete a user endpoint](https://paper-attachments.dropbox.com/s_F59F3317E88EE92046EF41C74313DA1EE5A1440BFF5C32BE319073C080AE6029_1653085621234_Screenshot+2022-05-20+at+23.18.05.png)

![Get list of users endpoint](https://paper-attachments.dropbox.com/s_F59F3317E88EE92046EF41C74313DA1EE5A1440BFF5C32BE319073C080AE6029_1653085635850_Screenshot+2022-05-20+at+23.17.44.png)



![Database with users document](https://paper-attachments.dropbox.com/s_F59F3317E88EE92046EF41C74313DA1EE5A1440BFF5C32BE319073C080AE6029_1653181925902_Screenshot+2022-05-22+at+02.10.29.png)

# Conclusion

This post discussed how to modularize a Rust application, build a REST API, and persist our data using MongoDB.

You may find these resources helpful:

- [Actix web](https://actix.rs/)
- [MongoDB Rust Driver](https://github.com/mongodb/mongo-rust-driver)
- [Serde (Serializing and Deserializing library)](https://serde.rs/)
- [Rust Environment Loader](https://crates.io/crates/dotenv)