A simple API application using Python & Flask
Tags: python, flask, api, basics
Published: 2021-09-24 10:11:56


How to create a simple API application with minimal learning and clean code for beginners.

Most of the times developers create a simple frontend application that needs a couple of API's but to do that they might rely on the backend team or mock API tools like Postman or create their own. 
So let us write a tiny application which covers this requirement.

##Why flask?

We do have multiple choices to make a minimal API setup,

1. NodeJS with Express
2. Python with Flask or Django
3. C# with .Net Core or
4. Ruby on Rails.

We can use any of the above but I am an old friend of Ruby on Rails so chose Python with Flask for a bare minimum code. ( Actually less than 50 lines :eyes:)

![Python Flask](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ngsdtk2uxa0t5m9s0w69.png)

This article doesn't intended to be production-ready code, but to make quick APIs with a database for beginners.

>What we are going to cover

>>1. System setup
>>2. Project setup
>>3. Implementation
>>4. Testing the API's

Okey, lets start to code :fire:

##1. System setup - prerequisites

```ruby
Mac OS and Python 3.X, VS Code, Postgresql and Git.
```

If you are using Ubuntu then the flow will be same but need to find the equivalent commands in installation.


#2. Project setup
####2.1 Install / Verify the python

```python
brew install python
```

Check the installation

```python
python --version
```
####2.2 Install virtualenv

To manage the python and dependencies across the system at least on the dev environment we must use the virtualenv

```python
pip install virtualenv
```

####2.3 Project initiation

Create a folder for the project

```python
mkdir flash-sample
cd flask-sample
ls
__pycache__ env
```

####2.4. virtualenv initialization

```python
python -m venv env
source env/bin/activate
```

####2.5 install SQL ORM

In this article, we will use sqlalchemy with PostgreSQL

```python
pip install flask_sqlalchemy
pip install psycopg2
```

####2.6 Create project requirements

```python
python -m pip freeze requirements.txt

click==8.0.1
Flask==2.0.1
Flask-SQLAlchemy==2.5.1
greenlet==1.1.1
itsdangerous==2.0.1
Jinja2==3.0.1
MarkupSafe==2.0.1
psycopg2==2.9.1
SQLAlchemy==1.4.25
Werkzeug==2.0.1
```
####2.7 Version control

We will use the git as version control and let's initialize

```ruby
git init.
Initialized empty Git repository in /PROJECT_FOLDER/.git/

git s
  On branch master
  No commits yet
  Untracked files:
  (use "git add file..." to include in what will be committed)

  env/
  requirements.txt
  nothing added to commit but untracked files present (use "git add" to track)
```

Create gitignore file to remove the unwanted files tobe tracked and pushed to the repository

```ruby
touch .gitignore
```

Add the standard gitignore file from [here](https://github.com/github/gitignore/blob/master/Python.gitignore) and make sure you add the virtualenv folder name in the gitignore.

```ruby
git add.
git commit -m "Initial commit"
```

So the base code is ready now.

##3. Implementation

As mentioned earlier in this article we are going to add two rest API endpoints to save and retrieve data using Postgresql.

Create the python file to handle our API's

```ruby
touch app.py
```

####3.1 Import section

Import required modules

```python
from flask import Flask, request
from flask_sqlalchemy import SQLAlchemy
from flask import jsonify
```

####3.2 setup database configurations

```python
app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'postgresql:///hands_contact'
app.config["SQLALCHEMY_TRACK_MODIFICATIONS"] = False
db = SQLAlchemy(app)
```

If PostgreSQL running in a remote location then we can add the URL along with user name and password like

```
'postgresql://usernamr:password@server/database_name'
```

####3.3 Define the model

Let's create a user model

```python
class SampleUser(db.Model):
  id = db.Column(db.Integer, primary_key=True)
  first_name = db.Column(db.String(80), nullable=False)
  last_name = db.Column(db.String(120), nullable=False)

  def __init__(self, first_name, last_name):
    self.first_name = first_name
    self.last_name = last_name
```

####3.4 Invoke the app

Let's create a module to run the application and set the database with models

```python
if __name__ == '__main__':
  db.create_all()
  app.run()
```

####3.5 Create the users

```python
def creation():

  content=request.get_json(silent=True)
  try:
    db.session.add(SampleUser(content["first_name"],
    content["last_name"]))
    db.session.commit()
    return'Ok', 200
  exceptExceptionase:
    return'Invalid request', 400
```

The request will be converted into JSON and required parameters are loaded from there to create the user.

Finally, mound this method into the route using

```python
@app.route("/create_users", methods=['POST'])
```

####3.6 List users

Similar to the creation let's create a get users route as GET method.

```python
@app.route('/users', methods=['GET'])
def home():<br /> data = []
for user in SampleUser.query.all():
  data.append(
    { "id": user.id,
      "first_name": user.first_name,
      "last_name": user.last_name
    })
  return jsonify(total_users_count=len(data), data=data, status=200)
```

To be basic used the jsonify but to be more proper we can use the marshmallow serializer.

Import

```python
from flask_marshmallow import Marshmallow
```

Initialize

```python
ma = Marshmallow(app)
```

Setup a schema that defined what are the columns needs to be constructed in JSON

```python
class SampleUserSchema(ma.Schema):
  class Meta:
    fields = ("id", "first_name")

sample_user_schema = SampleUserSchema()
sample_user_schema = SampleUserSchema(many=True)
```

Now update the get users method.

```python
@app.route('/users', methods=['GET'])
def listing():
  all_notes = SampleUser.query.all()
  return jsonify(sample_user_schema.dump(all_notes)), 200
```

that's it! so simple isn't it? The complete code for convenience below

```python
from flask import Flask, request
from flask_sqlalchemy import SQLAlchemy
from flask_marshmallow import Marshmallow
from flask import jsonify

# Database ORM Configs
app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'postgresql:///hands_contact'
app.config["SQLALCHEMY_TRACK_MODIFICATIONS"] = False
db = SQLAlchemy(app)
ma = Marshmallow(app)

# User model for contact us
class SampleUser(db.Model):
  id = db.Column(db.Integer, primary_key=True)
  first_name = db.Column(db.String(80), nullable=False)
  last_name = db.Column(db.String(120), nullable=False)
  def __init__(self, first_name, last_name):
    self.first_name = first_name
    self.last_name = last_name

# JSON response serializer
class SampleUserSchema(ma.Schema):
  class Meta:
    fields = ("id", "first_name")
sample_user_schema = SampleUserSchema()
sample_user_schema = SampleUserSchema(many=True)

# Simple Dashboard
@app.route('/users', methods=['GET'])
def listing():
  all_notes = SampleUser.query.all()
  return jsonify(sample_user_schema.dump(all_notes)), 200

# Contact us POST API
@app.route("/create_users", methods=['POST'])
def creation():
  content = request.get_json(silent=True)
  try:
    db.session.add(SampleUser(content["first_name"], content["last_name"]))
    db.session.commit()
    return 'Ok', 200
  except Exception as e:
    return 'Invalid request', 400

# Invoke the application
if __name__ == '__main__':
    db.create_all()
    app.run()
```

##4.Let's test now

Run the application from terminal

```python
flask run
```
The default port for flask application is 5000 and applictaion will be listening to http://127.0.0.1:5000.

Create users using the POST endpoint /create_users with the payload

```json
curl -X POST -d '{ "first_name": "Badsha", "last_name": "Manik" }' http://127.0.0.1:5000/create_users -H 'Content-Type: application/json' -H 'Accept: application/json'

Ok
```

List users using GET endpoint /users 

```json
curl http://127.0.0.1:5000/users

[{"first_name":"Vijay","id":1},{"first_name":"" 1","id":2},{"first_name":"Ajith","id":3}]
```

So as a starting point this single file serves the purpose but for production, as per required patterns you can split the configs, serializer, models and API methods and import wherever required.

References,
<https://flask.palletsprojects.com/en/2.0.x/>
<https://flask.palletsprojects.com/en/2.0.x/api/>
<https://flask-marshmallow.readthedocs.io/en/latest/>
<br/>
Thank you for your time and happy learning!!!