Build A Laundry CRUD API with FastAPI using MongoDB - 1
Tags: python, mongodb
Published: 2020-09-20 12:15:50

Building a Laundry CRUD API using FastAPI with the support of MongoDB as the database technology! I often use cloud storage like MongoDB in my projects to provide protection, the most reliable backups system, easy access to your data from anywhere, and most importantly is its low cost of access.

## Table Of Contents
* [Basic CRUD in a Laundry API](#chapter-1)
* [Installing FastAPI](#chapter-2)
* [Setting up MongoDB Atlas](#chapter-3)
* [Building each API Endpoints](#chapter-4)

*Grab your popcorn, sit comfortably and let's go* 🚀

But wait... Do you have a basic knowledge using MongoDB? Have you written programs in Python? If YES!! Then you're a good fit to continue here. What if No! Don't Panic I got you covered by simplifying every single step in this article.

#### MongoDB??
Curious about what MongoDB is? or how it's being used?
MongoDB is a document-oriented database program which requires no SQL, and stores data in JSON-like formats.

#### FastAPI??
FastAPI is a modern, **fast** framework for building APIs with Python. Note that I had to bold the word **fast**! As the name implies "FastAPI", it is very fast and easy to code and automatically generates well swagger documentation for your API. That's sweet right! I know you will like this. 

*Get a drink and have a sip, Let's move* 🚀

### Basic CRUD in a Laundry API <a name="chapter-1"></a>
A laundry service API should require the following basic CRUD(Create Read Update Delete) functions in managing bookings of services(laundry pickups, Updating status of service, canceling pickups, and so on).

Since this article is part one in building a laundry service API, we will focus on login/signup API endpoints(with no authentications yet).

Endpoints to be built in this article includes:
- Login
- signup
- update-profile
- book-service
- update-pickup



### Installing FastAPI <a name="chapter-2"></a>
After highlighting the endpoints we're working on in this article, let's install FastAPI! *You should have VSCode and your preferred browser ready(I use Chrome)*.

Firstly, open a folder(preferably on your desktop making it easy to locate), name it "laundryAPI".
Open the folder with VSCode and create a file "main.py"

Now open your VSCode Terminal and type this:
*Note: You can create a virtual environment but not necessary*
`pip install fastapi`

You will need an ASGI server for production such as *Uvicorn*:
`pip install uvicorn`

After successfully installing FastAPI, let's write our first API program for our root endpoint(homepage):
In the main.py, type the following code;
```
from typing import Optional

from fastapi import FastAPI

app = FastAPI()


@app.get("/")
def index():
    return {"message": "Hello World"}
```
Let's run and test our API:
type this in the terminal to run the code;
`uvicorn main:app --reload`
Our API should be running on [http://127.0.0.1:8000](http://127.0.0.1:8000)

We successfully built our API 🎉🎉

### Setting up MongoDB Atlas <a name="chapter-3"></a>
We successfully wrote our first FastAPI program! You see how fast and easy it is? Very fast!! I know you like that! Now, let's set up our MongoDB Atlas to store our laundry service data.

- Signup for a free MongoDB cloud storage [here](https://www.mongodb.com/cloud/atlas/signup)
- After signup, create a project,  name it 'laundrystore'

![You should see this now](https://i.ibb.co/Hp91d1g/Screenshot-179.png)

- Click build a cluster to create an Atlas:*free atlas*!
*New clusters take 1-3 minutes to provision*
- After the cluster has been created, click on 'connect' for details on how to connect your cloud storage to your app!
- Set your cloud storage access permissions to allow connection from anywhere!
- Set a database username and password, and choose a connection method!

![Choose a connection Method](https://i.ibb.co/LJk6vpS/Screenshot-180.png)

- For this API, choose "Connect your application" and copy the connection string provided after selecting Python as the driver to use it!

*Keep your connection string, we will use it in our API development! Do not forget your database username and password*
*We have successfully created and setup our MongoDB Atlas! We now have cloud storage for our Laundry API*

### Building each API Endpoints <a name="chapter-4"></a>
Let's work on the endpoints highlighted for this part one article:
starting with the signup endpoint!

*In this article, we will be using schematics to organize and validate our data model types and structures.*

Install schematics:
`pip install schematics`

create a `settings.py` file and type the following code:

```
# MongoDB attributes
mongodb_uri = 'mongodb+srv://kenny:123CloudLaundry@cluster0.5xmt6.mongodb.net/<usersdata>?retryWrites=true&w=majority'
port = 8000
```

*Note that I replaced some strings in the connection string we copied from MongoDB with the database username and password we created*

create a `connection.py` file and type the following code:

```
from pymongo import MongoClient
import settings

client = MongoClient(settings.mongodb_uri, settings.port)
db = client['usersdata']
```
*This is basically a connection file that creates a connection with MongoClient! Also, a database called 'usersdata'*

Now, update your `main.py` file with the following code:

```
from typing import Optional

from fastapi import FastAPI
import connection
from bson import ObjectId
from schematics.models import Model
from schematics.types import StringType, EmailType


class User(Model):
    user_id = ObjectId()
    email = EmailType(required=True)
    name = StringType(required=True)
    password = StringType(required=True)

# An instance of class User
newuser = User()

# funtion to create and assign values to the instanse of class User created
def create_user(email, username, password):
    newuser.user_id = ObjectId()
    newuser.email = email
    newuser.name = username
    newuser.password = password
    return dict(newuser)

app = FastAPI()


# Our root endpoint
@app.get("/")
def index():
    return {"message": "Hello World"}

# Signup endpoint with the POST method
@app.post("/signup/{email}/{username}/{password}")
def signup(email, username: str, password: str):
    user_exists = False
    data = create_user(email, username, password)

    # Covert data to dict so it can be easily inserted to MongoDB
    dict(data)

    # Checks if an email exists from the collection of users
    if connection.db.users.find(
        {'email': data['email']}
        ).count() > 0:
        user_exists = True
        print("USer Exists")
        return {"message":"User Exists"}
    # If the email doesn't exist, create the user
    elif user_exists == False:
        connection.db.users.insert_one(data)
        return {"message":"User Created","email": data['email'], "name": data['name'], "pass": data['password']}
```

*-I will explain what we did there, but that is simply an implementation of the signup endpoint without hashing our password values!*
*-We will also learn how to hash our passwords before storing them into the cloud!*
*-The Signup endpoint simply creates a new user into the MongoDB storage and returns information about the user-created or either returns a piece of information that the user exists!*

Type `uvicorn main:app --reload` to run the code!

Now visit [http://127.0.0.1:8000/docs](http://127.0.0.1:8000/docs) from your browser to use the FastAPI swagger UI to test our API.

This part one article is basically for us to learn for CRUD works and how it's being implemented! In the next article, we will learn how to professionally structure our code and folders! Professionally structuring our code simply means how to reuse codes! Qualities of a good program are the ability to reuse codes, class, functions, and so on.


Implementing the login endpoint: for the sake of python beginners, we're not using packages for our logins and signups!

Update your `main.py` file with the following code(updated with the login endpoint)

```
from typing import Optional

from fastapi import FastAPI
import connection
from bson import ObjectId
from json import dumps
from schematics.models import Model
from schematics.types import StringType, EmailType


class User(Model):
    user_id = ObjectId()
    email = EmailType(required=True)
    name = StringType(required=True)
    password = StringType(required=True)

# An instance of class User
newuser = User()

# funtion to create and assign values to the instanse of class User created
def create_user(email, username, password):
    newuser.user_id = ObjectId()
    newuser.email = email
    newuser.name = username
    newuser.password = password
    return dict(newuser)

# A method to check if the email parameter exists from the users database before validation of details
def email_exists(email):
    user_exist = True

    # counts the number of times the email exists, if it equals 0 it means the email doesn't exist in the database
    if connection.db.users.find(
        {'email': email}
    ).count() == 0:
        user_exist = False
        return user_exist

# Reads user details from database and ready for validation
def check_login_creds(email, password):
    if not email_exists(email):
        activeuser = connection.db.users.find(
            {'email': email}
        )
        for actuser in activeuser:
            actuser = dict(actuser)
            # Converted the user ObjectId to str! so this can be stored into a session(how login works)
            actuser['_id'] = str(actuser['_id'])    
            return actuser


app = FastAPI()


# Our root endpoint
@app.get("/")
def index():
    return {"message": "Hello World"}


# Signup endpoint with the POST method
@app.post("/signup/{email}/{username}/{password}")
def signup(email, username: str, password: str):
    user_exists = False
    data = create_user(email, username, password)

    # Covert data to dict so it can be easily inserted to MongoDB
    dict(data)

    # Checks if an email exists from the collection of users
    if connection.db.users.find(
        {'email': data['email']}
        ).count() > 0:
        user_exists = True
        print("USer Exists")
        return {"message":"User Exists"}
    # If the email doesn't exist, create the user
    elif user_exists == False:
        connection.db.users.insert_one(data)
        return {"message":"User Created","email": data['email'], "name": data['name'], "pass": data['password']}

# Login endpoint
@app.get("/login/{email}/{password}")
def login(email, password):
    def log_user_in(creds):
        if creds['email'] == email and creds['password'] == password:
            return {"message": creds['name'] + ' successfully logged in'}
        else:
            return {"message":"Invalid credentials!!"}
    # Read email from database to validate if user exists and checks if password matches
    logger = check_login_creds(email, password)
    if bool(logger) != True:
        if logger == None:
            logger = "Invalid Email"
            return {"message":logger}
    else:
        status = log_user_in(logger)
        return {"Info":status}
```

*-This code is instructing enough with the comments!*
*-The login endpoint uses the `check_login_creds(), email_exists(), log_user_in()` methods to: check if the user exists, then validates the users details and logs the user in if values matches*

![Tests Login Endpoint](https://i.ibb.co/wY29yWt/Screenshot-181.png)

*The above image shows the result returned from the API when credentials for login isn't correct!*
*In the next article, we will also learn how to handle errors so our API doesn't break! for example when the connection to MongoDB is timed out, the API returns a 500 response code(application error)! It's more professional and advisable to use error handlings in codes so the application doesn't break!*

### Summary
In this article, we tried to make it as simplified as possible for every individual to understand!
No authentications, sessions, or any complicated method in building an API!
You can get the code [here](https://github.com/Totoola-Kehinde/laundryAPI)

Star the repo and follow me to!

*Happy learning*🎉🎉🎉