Building an Employee Management System using FastAPI and Pydantic

In this blog post, we are going to build a simple Employee Management System using FastAPI and Pydantic. FastAPI is a modern, fast (high-performance), web framework for building APIs with Python based on standard Python type hints. Pydantic is a data validation and settings management using Python type annotations.

GitHub project: FastAPI Employee Management Application

Our application will be organized as a Python package called employee with five different files: utils.py, schemas.py, openapi.py, api.py, and main.py.

Utility Function

In utils.py, we define a utility function that returns the current date and time in UTC format:


from datetime import UTC, datetime

 
def utcnow() -> datetime:
    return datetime.now(tz=UTC)
      

Data Models with Pydantic

In schemas.py, we use Pydantic to define our data models. We have EmployeeCreatePayload for the data coming into our application and EmployeeCreated for the data going out of our application.


import uuid
from datetime import datetime
       
from pydantic import BaseModel, Extra, Field, PositiveFloat, UUID4
          
from .utils import utcnow
      
      
class EmployeeCreatePayload(BaseModel):
    name: str
    position: str
    salary: PositiveFloat
          
    class Config:
        extra = Extra.forbid
      
           
class EmployeeCreated(BaseModel):
    id: UUID4 = Field(default_factory=uuid.uuid4)
    name: str
    position: str
    salary: float
    created_at: datetime = Field(default_factory=utcnow)
      
      

OpenAPI Response Types

In openapi.py, we use FastAPI's in-built support for OpenAPI to define our response types:


from typing import Any, TypeAlias

from fastapi import status
from pydantic import BaseModel
  
OpenAPIResponseType: TypeAlias = dict[int | str, dict[str, Any]]
  
  
class ErrorModel(BaseModel):
    detail: str
  
  
EMPLOYEE_NOT_FOUND: OpenAPIResponseType = {
    status.HTTP_404_NOT_FOUND: {
        "model": ErrorModel,
        "content": {
            "application/json": {
                "examples": {
                    status.HTTP_404_NOT_FOUND: {
                        "summary": "Employee not found",
                        "value": {
                            "detail": "No employee found with the provided ID:"
                            " '7c926000-613b-468e-a17d-a3fa1e3ef0e8'."
                        },
                    },
                },
            },
        },
    },
}
  
      
      

API Endpoints with FastAPI

In api.py, we define our API endpoints using FastAPI's APIRouter. We have endpoints for creating an employee, retrieving a list of all employees, retrieving a specific employee by ID, and deleting all employees.


from fastapi import APIRouter, HTTPException, status
from pydantic import UUID4

from employee import schemas
from employee.openapi import EMPLOYEE_NOT_FOUND

router = APIRouter(prefix="/employees", tags=["employees"])

EMPLOYEES = {}


@router.post(path="", status_code=status.HTTP_201_CREATED)
def create_employee_endpoint(
   payload: schemas.EmployeeCreatePayload,
) -> schemas.EmployeeCreated:
   new_employee = schemas.EmployeeCreated(
       name=payload.name,
       position=payload.position,
       salary=payload.salary,
   )
   EMPLOYEES[new_employee.id] = new_employee
   return new_employee


@router.get(path="")
def list_employees_endpoint() -> list[schemas.EmployeeCreated]:
   return list(EMPLOYEES.values())


@router.delete(path="", status_code=status.HTTP_204_NO_CONTENT)
def clear_all_employees_endpoint() -> None:
   EMPLOYEES.clear()


@router.get(path="/{employee_id}", responses=EMPLOYEE_NOT_FOUND)
def retrieve_employee_endpoint(employee_id: UUID4) -> schemas.EmployeeCreated:
   try:
       return EMPLOYEES[employee_id]
   except KeyError:
       raise HTTPException(
           status_code=status.HTTP_404_NOT_FOUND,
           detail=f"No employee found with the provided ID: '{employee_id}'.",
       )
      
      

Main application

Finally, in main.py, we create our FastAPI application and include the router from api.py:


from fastapi import FastAPI

from employee.api import router

app = FastAPI()
app.include_router(router=router)
      
      

Running the Application

To run this project, you need Python 3.11 or higher. Clone the repository, navigate to the repo directory, and install the project using Poetry. You can then run the application with the command uvicorn main:app --app-dir employee --reload.

To interact with the API, open your browser and visit http://127.0.0.1:8000/docs

That's it! You've built a simple Employee Management System using FastAPI and Pydantic. This example should give you a good idea of how to structure your FastAPI projects and how to use Pydantic for data validation and serialization. Happy coding!

Bonus: Test Suite and README.md

In addition to the Employee Management System, the repository also includes a suite of tests to verify the functionality of the API. It's always a good practice to write tests for your code to ensure it's behaving as expected and to catch any issues or bugs early in the development process.

Moreover, the repository includes a detailed README.md file. This file provides all the necessary information about the project, including the project structure, installation and usage instructions, and a description of the available API endpoints.

We strongly encourage you to read the README.md file in the repository and to run the tests to get a better understanding of the application and to ensure everything is working correctly. Happy learning and coding!