Create a GraphQL-powered project management endpoint in Rust and MongoDB - Actix web version
Tags: rust, graphql, mongodb, actixweb
Published: 2022-07-25 09:14:54

---
title: Create a GraphQL-powered project management endpoint in Rust and MongoDB - Actix web version
published: true
description: This post will discuss building a project management application with Rust using the Async-graphql library and MongoDB.
tags: #Rust #GraphQL #MongoDB #ActixWeb
cover_image: https://dev-to-uploads.s3.amazonaws.com/uploads/articles/yj89te0q7en44xcyr8md.png
# Use a ratio of 100:42 for best results.
---
GraphQL is a query language for reading and manipulating data for APIs. It prioritizes giving clients or servers the exact data requirement by providing a flexible and intuitive syntax to describe such data. 

Compared to a traditional REST API, GraphQL provides a type system to describe schemas for data and, in turn, gives consumers of the API the affordance to explore and request the needed data using a single endpoint.

This post will discuss building a project management application with Rust using the [Async-graphql](https://github.com/async-graphql/async-graphql) library and [MongoDB](https://www.mongodb.com/). At the end of this tutorial, we will learn how to create a GraphQL endpoint that supports reading and manipulating project management data and persisting our data using MongoDB.
GitHub repository can be found [here](https://github.com/Mr-Malomz/project-mngt-rust-graphql-actix).

# 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:

- Basic knowledge of GraphQL
- A [MongoDB account](https://www.mongodb.com/) to host the database. [**Signup**](https://www.mongodb.com/cloud/atlas/register) **is completely free**

# 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 project-mngt-rust-graphql-actix && cd project-mngt-rust-graphql-actix
```

This command creates a Rust project called  `project-mngt-rust-graphql-actix` 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"
    async-graphql = { version = "4.0", features = ["bson", "chrono"] }
    async-graphql-actix-web = "4.0"
    serde = "1.0.136"
    dotenv = "0.15.0"
    futures = "0.3"
    
    [dependencies.mongodb]
    version = "2.2.0"
```
`actix-web = "4"` is a Rust based framework for building web application.

`async-graphql = { version = "4.0", features = ["bson", "chrono"] }` is a server-side library for building GraphQL in Rust. It also features `bson` and `chrono`.

`async-graphql-actix-web = "4.0"` is a library that helps integrate `async-grapql` with `actix web`.

`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.

We need to run the command below to install the dependencies:
```bash
    cargo build
```

## Module system in Rust

Modules are like folder structures in our application; they simplify how we manage dependencies.

To do this, we need to navigate to the `src` folder and create the  `config`, `handler`, and `schemas` folder with their corresponding `mod.rs`  file to manage visibility.

![Updated project folder structure](https://paper-attachments.dropbox.com/s_AB562E5C2E35FE5CD8B7C6C6E910200C49A248F6538EF2E3334B4E983D325E2E_1658499586796_Screenshot+2022-07-22+at+15.19.18.png)


`config` is for modularizing configuration files.

`handler` is for modularizing GraphQL logics.

`schemas` is for modularizing GraphQL schema.

**Adding a 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 modules
    mod config;
    mod handler;
    mod schemas;
    
    fn main() {
        println!("Hello, world!");
    }
```

## 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 `projectMngt` as the project name, click **Next**, and click **Create Project..**

![enter project name](https://paper-attachments.dropbox.com/s_5DB793BEFC159F569DA013C68231051E6736BBB84507D7CF5CD2A8277968E59C_1657388688286_Screenshot+2022-07-09+at+18.44.22.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_0E637C74C0D58C5FC3FBA1D21E2CF4F11E7592B7929DAE10790A9D12FE972D1D_1640511574285_Screenshot+2021-12-26+at+10.38.59.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_0E637C74C0D58C5FC3FBA1D21E2CF4F11E7592B7929DAE10790A9D12FE972D1D_1640512109914_Screenshot+2021-12-26+at+10.43.36.png)
![Add IP](https://paper-attachments.dropbox.com/s_0E637C74C0D58C5FC3FBA1D21E2CF4F11E7592B7929DAE10790A9D12FE972D1D_1640512118747_Screenshot+2021-12-26+at+10.43.52.png)


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

![Database Screen](https://paper-attachments.dropbox.com/s_AB562E5C2E35FE5CD8B7C6C6E910200C49A248F6538EF2E3334B4E983D325E2E_1658506986553_Screenshot+2022-07-22+at+17.22.35.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_AB562E5C2E35FE5CD8B7C6C6E910200C49A248F6538EF2E3334B4E983D325E2E_1658507076696_Screenshot+2022-07-22+at+17.22.35.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_AB562E5C2E35FE5CD8B7C6C6E910200C49A248F6538EF2E3334B4E983D325E2E_1658507174205_Screenshot+2022-07-22+at+17.22.53.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 below:

```bash
    MONGOURI=mongodb+srv://<YOUR USERNAME HERE>:<YOUR PASSWORD HERE>@cluster0.e5akf.mongodb.net/<DATABASE NAME>?retryWrites=true&w=majority
```
Sample of a properly filled connection string below:
```bash
   MONGOURI=mongodb+srv://malomz:malomzPassword@cluster0.e5ahghkf.mongodb.net/projectMngt?retryWrites=true&w=majority
```

## Creating GraphQL Endpoints

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

```rust
    use async_graphql::{Enum, InputObject, SimpleObject};
    use mongodb::bson::oid::ObjectId;
    use serde::{Deserialize, Serialize};
    
    //owner schema
    #[derive(Debug, Clone, Serialize, Deserialize, SimpleObject)]
    pub struct Owner {
        #[serde(skip_serializing_if = "Option::is_none")]
        pub _id: Option<ObjectId>,
        pub name: String,
        pub email: String,
        pub phone: String,
    }
    
    #[derive(InputObject)]
    pub struct CreateOwner {
        pub name: String,
        pub email: String,
        pub phone: String,
    }
    
    #[derive(InputObject)]
    pub struct FetchOwner {
        pub _id: String,
    }
    
    //project schema
    #[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Enum)]
    pub enum Status {
        NotStarted,
        InProgress,
        Completed,
    }
    
    #[derive(Debug, Clone, Serialize, Deserialize, SimpleObject)]
    pub struct Project {
        #[serde(skip_serializing_if = "Option::is_none")]
        pub _id: Option<ObjectId>,
        pub owner_id: String,
        pub name: String,
        pub description: String,
        pub status: Status,
    }
    
    #[derive(InputObject)]
    pub struct CreateProject {
        pub owner_id: String,
        pub name: String,
        pub description: String,
        pub status: Status,
    }
    
    #[derive(InputObject)]
    pub struct FetchProject {
        pub _id: String,
    }
```

The snippet above does the following:

- Imports required dependencies
- Uses the `derive` macro to generate implementation support for `Owner`, `CreateOwner`, `FetchOwner`, `Status`, `Project`, `CreateProject`, and `FetchProject`. The snippet also uses the procedural macro from the `serde` and `async-graphql` library to  serialize/deserialize and convert Rust structs to a GraphQL schema.

Next, we must register the `project_schema.rs` file as part of the `schemas` module. To do this, open the `mod.rs` in the `schemas` folder and add the snippet below:
```rust
    pub mod project_schema;
```
**Database Logic**
With the schema fully set up and made available to be consumed, we can now create our database logic that will do the following:

- Create project owner
- Get all owners
- Get a single owner
- Create project
- Get all projects
- Get a single project

To do this, First, we need to navigate to the `config` folder, and in this folder, create a `mongo.rs` file and add the snippet below:
```rust
    use dotenv::dotenv;
    use futures::TryStreamExt;
    use std::{env, io::Error};
    use mongodb::{
        bson::{doc, oid::ObjectId},
        Client, Collection, Database,
    };
    use crate::schemas::project_schema::{Owner, Project};
    
    pub struct DBMongo {
        db: Database,
    }
    
    impl DBMongo {
        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)
                .await
                .expect("error connecting to database");
            let db = client.database("projectMngt");
            DBMongo { db }
        }
    
        fn col_helper<T>(data_source: &Self, collection_name: &str) -> Collection<T> {
            data_source.db.collection(collection_name)
        }
    }
```
The snippet above does the following:

- Imports the required dependencies
- Creates a `DBMongo` struct with a `db` field to access MongoDB database
- Creates an implementation block that adds methods to the `DBMongo` 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 `DBMongo` struct
- Adds a `col_helper` method; a helper function to create MongoDB collection

Next, we need to add the remaining methods to the `DBMongo` implementation to cater to the project management operations:
```rust
    //imports goes here
    
    pub struct DBMongo {
        db: Database,
    }
    
    impl DBMongo {
        pub async fn init() -> Self {
            //init code goes here
        }
    
        fn col_helper<T>(data_source: &Self, collection_name: &str) -> Collection<T> {
            data_source.db.collection(collection_name)
        }
    
        //Owners logic
        pub async fn create_owner(&self, new_owner: Owner) -> Result<Owner, Error> {
            let new_doc = Owner {
                _id: None,
                name: new_owner.name.clone(),
                email: new_owner.email.clone(),
                phone: new_owner.phone.clone(),
            };
            let col = DBMongo::col_helper::<Owner>(&self, "owner");
            let data = col
                .insert_one(new_doc, None)
                .await
                .expect("Error creating owner");
            let new_owner = Owner {
                _id: data.inserted_id.as_object_id(),
                name: new_owner.name.clone(),
                email: new_owner.email.clone(),
                phone: new_owner.phone.clone(),
            };
            Ok(new_owner)
        }
    
        pub async fn get_owners(&self) -> Result<Vec<Owner>, Error> {
            let col = DBMongo::col_helper::<Owner>(&self, "owner");
            let mut cursors = col
                .find(None, None)
                .await
                .expect("Error getting list of owners");
            let mut owners: Vec<Owner> = Vec::new();
            while let Some(owner) = cursors
                .try_next()
                .await
                .expect("Error mapping through cursor")
            {
                owners.push(owner)
            }
            Ok(owners)
        }
    
        pub async fn single_owner(&self, id: &String) -> Result<Owner, Error> {
            let obj_id = ObjectId::parse_str(id).unwrap();
            let filter = doc! {"_id": obj_id};
            let col = DBMongo::col_helper::<Owner>(&self, "owner");
            let owner_detail = col
                .find_one(filter, None)
                .await
                .expect("Error getting owner's detail");
            Ok(owner_detail.unwrap())
        }
    
        //project logics
        pub async fn create_project(&self, new_project: Project) -> Result<Project, Error> {
            let new_doc = Project {
                _id: None,
                owner_id: new_project.owner_id.clone(),
                name: new_project.name.clone(),
                description: new_project.description.clone(),
                status: new_project.status.clone(),
            };
            let col = DBMongo::col_helper::<Project>(&self, "project");
            let data = col
                .insert_one(new_doc, None)
                .await
                .expect("Error creating project");
            let new_project = Project {
                _id: data.inserted_id.as_object_id(),
                owner_id: new_project.owner_id.clone(),
                name: new_project.name.clone(),
                description: new_project.description.clone(),
                status: new_project.status.clone(),
            };
            Ok(new_project)
        }
    
        pub async fn get_projects(&self) -> Result<Vec<Project>, Error> {
            let col = DBMongo::col_helper::<Project>(&self, "project");
            let mut cursors = col
                .find(None, None)
                .await
                .expect("Error getting list of projects");
            let mut projects: Vec<Project> = Vec::new();
            while let Some(project) = cursors
                .try_next()
                .await
                .expect("Error mapping through cursor")
            {
                projects.push(project)
            }
            Ok(projects)
        }
    
        pub async fn single_project(&self, id: &String) -> Result<Project, Error> {
            let obj_id = ObjectId::parse_str(id).unwrap();
            let filter = doc! {"_id": obj_id};
            let col = DBMongo::col_helper::<Project>(&self, "project");
            let project_detail = col
                .find_one(filter, None)
                .await
                .expect("Error getting project's detail");
            Ok(project_detail.unwrap())
        }
    }
```
The snippet above does the following:

- Adds a `create_owner` method that takes in a `self` and `new_owner` as parameters and returns the created owner or an error. Inside the method, we created a new document using the `Owner` struct. Then we use the `col_helper` method to create a new collection and access the `insert_one` function to create a new owner and handle errors. Finally, we returned the created owner information
- Adds a `get_owners` method that takes in a `self` as parameters and returns the list of owners or an error. Inside the method,  we use the `col_helper` method to create a new collection and access the `find` function without any filter so that it can match all the documents inside the database and returned the list optimally using the `try_next()` method to loop through the list of owners, and handle errors
- Adds a `single_owner` method that takes in a `self` and `id` as parameters and returns the owner detail or an error. Inside the method, we converted the `id` to an `ObjectId`  and used it as a filter to get the matching document. Then we use the `col_helper` method to create a new collection and access the `find_one` function from the collection to get the details of the owner and handle errors
- Adds a `create_project` method that takes in a `self` and `new_project` as parameters and returns the created project or an error. Inside the method, we created a new document using the `Project` struct. Then we use the `col_helper` method to create a new collection and access the `insert_one` function to create a new project and handle errors. Finally, we returned the created project information
- Adds a `get_projects` method that takes in a `self` as parameters and returns the list of projects or an error. Inside the method,  we use the `col_helper` method to create a new collection and access the `find` function without any filter so that it can match all the documents inside the database and returned the list optimally using the `try_next()` method to loop through the list of projects, and handle errors
- Adds a `single_project` method that takes in a `self` and `id` as parameters and returns the project detail or an error. Inside the method, we converted the `id` to an `ObjectId`  and used it as a filter to get the matching document. Then we use the `col_helper` method to create a new collection and access the `find_one` function from the collection to get the details of the project and handle errors

Finally, we must register the `mongo.rs` file as part of the `config` module. To do this, open the `mod.rs` in the `config` folder and add the snippet below:
```rust
    pub mod mongo;
```

**GraphQL Handlers**
With the database logic sorted out, we can start using them to create our GraphQL handlers. To do this, First, we need to navigate to the `handler` folder, and in this folder, create a `graphql_handler.rs` file and add the snippet below:
```rust
    use crate::{
        config::mongo::DBMongo,
        schemas::project_schema::{
            CreateOwner, CreateProject, FetchOwner, FetchProject, Owner, Project,
        },
    };
    use async_graphql::{Context, EmptySubscription, FieldResult, Object, Schema};
    
    pub struct Query;
    
    #[Object(extends)]
    impl Query {
        //owners query
        async fn owner(&self, ctx: &Context<'_>, input: FetchOwner) -> FieldResult<Owner> {
            let db = &ctx.data_unchecked::<DBMongo>();
            let owner = db.single_owner(&input._id).await.unwrap();
            Ok(owner)
        }
    
        async fn get_owners(&self, ctx: &Context<'_>) -> FieldResult<Vec<Owner>> {
            let db = &ctx.data_unchecked::<DBMongo>();
            let owners = db.get_owners().await.unwrap();
            Ok(owners)
        }
    
        //projects query
        async fn project(&self, ctx: &Context<'_>, input: FetchProject) -> FieldResult<Project> {
            let db = &ctx.data_unchecked::<DBMongo>();
            let project = db.single_project(&input._id).await.unwrap();
            Ok(project)
        }
    
        async fn get_projects(&self, ctx: &Context<'_>) -> FieldResult<Vec<Project>> {
            let db = &ctx.data_unchecked::<DBMongo>();
            let projects = db.get_projects().await.unwrap();
            Ok(projects)
        }
    }
    
    pub struct Mutation;
    
    #[Object]
    impl Mutation {
        //owner mutation
        async fn create_owner(&self, ctx: &Context<'_>, input: CreateOwner) -> FieldResult<Owner> {
            let db = &ctx.data_unchecked::<DBMongo>();
            let new_owner = Owner {
                _id: None,
                email: input.email,
                name: input.name,
                phone: input.phone,
            };
            let owner = db.create_owner(new_owner).await.unwrap();
            Ok(owner)
        }
    
        async fn create_project(
            &self,
            ctx: &Context<'_>,
            input: CreateProject,
        ) -> FieldResult<Project> {
            let db = &ctx.data_unchecked::<DBMongo>();
            let new_project = Project {
                _id: None,
                owner_id: input.owner_id,
                name: input.name,
                description: input.description,
                status: input.status,
            };
            let project = db.create_project(new_project).await.unwrap();
            Ok(project)
        }
    }
    
    pub type ProjectSchema = Schema<Query, Mutation, EmptySubscription>;
```
The snippet above does the following:

- Imports the required dependencies
- Creates a `Query` struct with implementation methods related to querying the database using the corresponding methods from the database logic
- Creates a `Mutation` struct with implementation methods related to modifying the database using the corresponding methods from the database logic.
- Creates a `ProjectSchema` type to construct how our GraphQL is using the `Query` struct, `Mutation` struct, and `EmptySubscription` since we don’t have any subscriptions.


**Creating GraphQL Server**
Finally, we can start creating our GraphQL server by integrating the `ProjectSchema` and MongoDB with Actix web. To do this, we need to navigate to the `main.rs` file and modify it as shown below:

```rust
    mod config;
    mod handler;
    mod schemas;
    
    //add 
    use actix_web::{
        guard,
        web::{self, Data},
        App, HttpResponse, HttpServer,
    };
    use async_graphql::{
        http::{playground_source, GraphQLPlaygroundConfig},
        EmptySubscription, Schema,
    };
    use async_graphql_actix_web::{GraphQLRequest, GraphQLResponse};
    use config::mongo::DBMongo;
    use handler::graphql_handler::{Mutation, ProjectSchema, Query};
    
    
    //graphql entry
    async fn index(schema: Data<ProjectSchema>, req: GraphQLRequest) -> GraphQLResponse {
        schema.execute(req.into_inner()).await.into()
    }
    
    async fn graphql_playground() -> HttpResponse {
        HttpResponse::Ok()
            .content_type("text/html; charset=utf-8")
            .body(playground_source(GraphQLPlaygroundConfig::new("/")))
    }
    
    #[actix_web::main]
    async fn main() -> std::io::Result<()> {
        //connect to the data source
        let db = DBMongo::init().await;
        let schema_data = Schema::build(Query, Mutation, EmptySubscription)
            .data(db)
            .finish();
        HttpServer::new(move || {
            App::new()
                .app_data(Data::new(schema_data.clone()))
                .service(web::resource("/").guard(guard::Post()).to(index))
                .service(
                    web::resource("/")
                        .guard(guard::Get())
                        .to(graphql_playground),
                )
        })
        .bind(("127.0.0.1", 8080))?
        .run()
        .await
    }
```
The snippet above does the following:

- Imports the required dependencies
- Creates an `index` function that uses the `ProjectSchema` type to create a GraphQL server
- Creates a `graphql_playground` function to create **GraphiQL**; a GraphQL playground we can access from a browser
- Uses the `#[actix_web::main]` macro to run the `main` function asynchronously within the actix runtime. The `main` function also does the following:
    - Creates a `db` variable to establish a connection to MongoDB by calling the `init()` method and uses it to build a GraphQL data
    - Creates a new server using `HttpServer` struct that uses a closure to serve incoming requests using the `App` instance that accepts the GraphQL data, add a `Post` service to manage all incoming GraphQL requests and a `Get` method to render the **GraphiQL** playground
    - Configures the server to run asynchronously and process HTTP requests on `localhost:8080`.


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

```bash
    cargo run
```
Then navigate to `127.0.0.1:8080` on a web browser.


![Create project](https://paper-attachments.dropbox.com/s_AB562E5C2E35FE5CD8B7C6C6E910200C49A248F6538EF2E3334B4E983D325E2E_1658529338277_Screenshot+2022-07-22+at+23.34.36.png)
![Get list of project](https://paper-attachments.dropbox.com/s_AB562E5C2E35FE5CD8B7C6C6E910200C49A248F6538EF2E3334B4E983D325E2E_1658529303179_Screenshot+2022-07-21+at+21.53.31.png)

![Get single owner](https://paper-attachments.dropbox.com/s_AB562E5C2E35FE5CD8B7C6C6E910200C49A248F6538EF2E3334B4E983D325E2E_1658529338231_Screenshot+2022-07-22+at+23.32.34.png)
![Create Owner](https://paper-attachments.dropbox.com/s_AB562E5C2E35FE5CD8B7C6C6E910200C49A248F6538EF2E3334B4E983D325E2E_1658529303204_Screenshot+2022-07-22+at+23.31.10.png)



![Database with data](https://paper-attachments.dropbox.com/s_AB562E5C2E35FE5CD8B7C6C6E910200C49A248F6538EF2E3334B4E983D325E2E_1658529546852_Screenshot+2022-07-22+at+08.54.40.png)

# Conclusion

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

These resources might be helpful:

- [Async-GraphQL](https://github.com/async-graphql/async-graphql)
- [Actix web](https://actix.rs/)
- [MongoDB Rust Driver](https://github.com/mongodb/mongo-rust-driver)
- [Async-GraphQL Actix integration](https://crates.io/crates/async-graphql-actix-web)
- [Build a REST API with Rust and MongoDB](https://dev.to/hackmamba/build-a-rest-api-with-rust-and-mongodb-actix-web-version-ei1)
- [Serde (Serializing and Deserializing library)](https://serde.rs/)