Know these before starting to write your REST API in Python
So, What is REST API?
REST ( Representative State Transfer) a.k.a RESTful API is an application programming interface that meets the requirements and limitations of the REST architectural style.
What makes an API RESTful?
In order of an API to be considered RESTful, the following architectural constraints have to be met:
- Client-Server Architecture: Enforces the separation of concerns and allows the components to evolve and independently
- Statelessness: Requests are made independent of one another, and are unconnected
- Cacheable: Streamlines Client Serve interactions and avoid stale inputs from users
- Layered System: Client components cannot tell if they connect to a server directly or through an intermediary, which could improve scalability (ex. proxies and load balancers) and security
- Code on Demand: Servers can send executable code to the clients, temporarily, to extend functionality
- Uniform Interface: Simplifies and decouples architecture allowing the components to evolve independently. The constraints to be met are: 1) Requested Resources are identifiable and separate from representation; 2) The client has the ability to manipulate representations they have received; 3)Messages returned to the client has enough information to process it; 4) The clients should be able to process hyperlinks from returned resource, to identify the actions available to them.
Characteristics of a well defined API?
- Well documented and easy to work with — Ex. Using OpenAPI specifications and documentations
- Hard to misuse
- Complete and Concise — Developers can make full applications from the data returned
- Uses JSON for sending and receiving data
- Uses Nouns for EndPoints — Verbs are already available as the methods (GET, POST, PUT, DELETE)
- Uses Plural Nouns for collections
- Uses Status Codes in Error Handling
- Uses Nesting of Endpoints to show hierarchal relationships
- Uses Filtering, Sorting, Paging, and Field selection — Actions that can be performed on a collection of a REST API
- Uses SSL for security
- Uses versioning of endpoints
Now, Let’s design an awesome REST API…
Now lets try to create an API that meets the expectations of the best practices
What is the API going to do?
We are going to create a simple billing app. The app:
- For new users, will create users
- Existing users can records transactions and obtain summaries of their transactions
Framework to create the API?
- FastAPI
- SqLite3
- Uvicorn
- SQLAlchemy
We have to create two sets of Classes, on for database connections and one for passing parameters using the API.
Liveness Check
- This endpoint passes the status_code. I have included here for the emphasis (have not included it in other endpoints).
@app.get("/v1/liveness", status_code=200)
async def liveness():
"""
This endpoint uses versioning and Nouns
This endpoint uses GET as no information is passed from the user.
When information are passed, it is advised to use POST, as the
parameters would be in the body instead of the URL.
"""
return {"App is live"}
User Model
First, I have created a for the “User” table. SqlAlchemy uses this class to insert and retrieve data from SQLite3
class User(Base):__tablename__ = "users"user_id = Column(Integer, primary_key=True, index=True, autoincrement=True)first_name = Column(String)last_name = Column(String)email = Column(String, unique=True, index=True)
The class is pretty straight forward. The database Table, “users”, has 4 columns with ‘user_id’ set as the primary key.
The class used by the API to create this entry is given below:
class UserCreate(BaseModel): first_name: str last_name: str email: str class Config:
orm_mode = True
This would be the class used to get the input parameters to make an entry into the ‘users’ table.
Transaction Model
Similarly, I define two classes for recording transactions and get the parameters
class Transaction(Base):
__tablename__ = "transactions"
user_email = Column(String, primary_key=True, index=True)
transaction_date = Column(Date, default=current_date)
transaction_item = Column(String)
transaction_quantity = Column(String)
item_unit_cost = Column(Float)
class TransactionCreate(BaseModel):
user_email: str
transaction_date: date
transaction_item: str
transaction_quantity: int
item_unit_cost: float
class Config:
orm_mode = True
EndPoint to create users
As per the best practices, the end point:
- uses small letters
- the endpoint is versioned. This makes the API maintainable and extendable
@app.post("/v1/create_user", response_model=UserCreate)
async def create_user(user: UserCreate,
db: Session = Depends(get_db)):
new_user = crud.register_user(db, user=user)
return new_user
EndPoint to obtain transaction records
- Each endpoint here does exactly one action. Making it difficult to be misused
- The different endpoints implement filtering to obtain data from the database
@app.post("/v1/transactions_by_entry_order")
async def get_transactions_entry_order(user_email: str,
from_entry_number: int,
to_entry_number: int,
db: Session=Depends(get_db)):
transaction = crud.get_transactions_by_entry_number(db,
user_email=user_email,
from_entry_number=from_entry_number,
to_entry_number=to_entry_number)
return transaction@app.post("/get_all_transactions")
async def get_all_transcations(user_email: str,
db: Session=Depends(get_db)):
"""
The endpoint doesn't use versioning, which will result in a lot of complications down the line during maintenance
"""
return crud.get_all_transactions(db, user_email=user_email)@app.post("/v1/transactions_by_date")
async def get_transcations_by_date(user_email: str,
from_date:str,
to_date:str,
db: Session=Depends(get_db)):
return crud.get_transactions_by_dates(db,
user_email=user_email,
from_date=from_date,
to_date=to_date)
API Documentation & Validations
In Flask, I used to create the API specifications myself. However, with FastAPI, this is already integrated making documentations easy. Further, it also goes very well with PyDantic for data validation.

FastAPI also provides another UI for documentation and experimentation.

Github Repo
For who wants to try the entire code, please feel free to visit my git repo: