💻
IT Documentation
  • 🥳Welcome!
  • General Concepts
    • SCRUM
      • Workflow
    • DevOps
      • What is DevOps?
      • What is TDD? (Test Driven Development)
      • What is CI? (Continuous Integration)
      • What is code coverage?
      • Linting best practices
      • Ephemeral Environments
      • Rolling Deployments
      • Blue/Geen Deployments
      • Canary Deployments
      • What is autoscaling & serverless?
      • What is service discovery?
      • What is Log Aggregation?
      • Metric Monitoring
  • AI
    • ChatGPT
      • Better prompts for ChatGPT
  • Cloud
    • AWS
      • AWS - 40 most common Services
      • AWS CLI Cheatsheet
      • Account & IAM
        • Create AWS Account
      • Lambda
        • Create Lambda function for Lightsail
      • Route 53
        • Set up R53 DNS Entry for GitBook
      • RDS
        • AWS RDS Burst Balance
      • VPC
        • What is a VPC
      • EC2
        • Removing EC2 user from sudo list
        • Create a Windows Gaming VPS
        • Connecting to a AWS EC2 instance
    • Azure
      • Courses
        • AZ900 Course
          • Overview
            • Describe Core Concepts
            • Cloud Models (IaaS, PaaS, SaaS)
          • Benefits of Cloud Computing
          • Cloud Models and Cloud Types
            • Overview of Cloud Models
            • IaaS, PaaS, SaaS
            • The Serverless Model
            • Cloud Types
          • Core Azure Architectural Components
            • Overview
            • Regions & Availability Zones
            • Resource Groups & Subscriptions & Management Groups
            • Resources & Resource Manager
          • Core Resources available
            • Compute Resources
            • Networking Resources
            • Storage Resources
            • Database Services
            • Azure Marketplace
          • Azure Core Solutions
            • Overview
            • Internet of Things (IoT) Solutions
            • Big Data Solutions
            • AI Solutions
            • Azure Functions & Logic Apps and Event Grid
            • DevOps Solutions
          • Azure Management Tools
            • Overview
            • Managing Azure with ARM Templates
            • Azure Monitor & Azure Service Health
          • Azure Security Features
            • Overview
            • Azure Security Center
            • Azure Key Vault & Azure Sentinel
            • Azure Dedicated Hosts
          • Azure Network Security
            • Network Security Group (NGS), Firewall and DDoS Protection
          • Azure Identity Services
            • Overview
            • Benefits
            • Multi-Factor Authentication (MFA)
            • Authentication vs Authorization
          • Azure Governance Features
            • Authentification and RBAC
            • Resource Locks
            • Azure Policy
            • Azure Blueprints
            • Cloud Adoption Framework (CAF)
          • Compliance Features
            • Core tenets of Security, Privacy and Compliance
            • Privacy Statement and Online Service Terms (OST)
            • Trust Center
            • Azure Sovereign Regions
          • Manage Azure Costs
            • Overview
            • Best practices
            • Pricing calculator
            • Azure Cost Management
          • SLA (Service level Agreements)
      • Virtual Machines
        • VM
          • Create a VM in a VNET
          • Azure VM LVM corruption fix
        • VMSS
          • Add SSH Key to VMSS
  • Containerization
    • Docker
      • Docker Cheatsheet
      • Install docker on Debian
      • Docker misc stuff
    • Docker Swarm
      • Docker Swarm Cheatsheet
      • Set up docker swarm
      • Delete docker swarm
      • Mount and bind volumes
      • Deploy Portainer via docker swarm
    • Docker Compose
      • Jenkins via Docker Compose
      • PostgreSQL via Docker Compose
      • Wireguard via Docker Compose & HTTPS
    • Rancher
      • Rancher installation guide
  • Cluster Computing
    • Slurm
      • Job Manager is not responding
      • Create new user
    • OpsCenter
      • Clear old Snapshots
      • Issues listing snapshots with nodetool
  • Database
    • General SQL
      • Database introduction
      • SQL 101
      • SQL Cheatsheet
      • User rights
      • Table Creation
      • SQL Replication - Best practice
      • SQL Database Design
    • MS SQL
      • Update whole table fast
    • Oracle SQL (PL/SQL)
      • Oracle SQL Cheatsheet
      • Oracle SQL - Kill Sessions
    • PostgreSQL
      • Install PostgreSQL
      • Create PostgreSQL Role and Database
      • Managing Postgres with PgAdmin GUI
      • Enable remote access for PostgreSQL
      • Authentication on PostgreSQL
      • Returning in Postgres
    • SQLite
    • Flyway Overview
    • SSRS Overview
    • Cassandra
      • GC OutOfMemoryError
  • DevOps
    • Ansible
      • Ansible Cheatsheet
      • Common Ansible Tasks
    • Git
      • Git 101
      • Git 1kb files
      • Git Commit changes before merge
      • Git Misc
      • Git Markdown
      • Git Clone Repo via SSH
    • Github
      • How to use multiple accounts
      • Delete commits fully
      • Set up git ignore file
    • Github Actions
      • Install self hosted runner
      • Scheduling jobs cron style
      • Passing ENV variable in script
      • SSH to Server
    • GitLab
      • GitLab Cheatsheet
    • Terraform
      • Terraform components
  • Hardware
    • UPS - Njoy
  • IoT
    • Home Assistant
      • Valetudo configs
      • Mini Media Player
      • HACS
    • Valetudo
      • Roborock Gen 1
      • Roborock quick guide
      • Roborock full Valetudo install guide
    • Tasmota
      • Tuya-Convert
    • LibreELEC
      • Quick LibreELEC guide
      • Configure X96 Mini Remote
  • OS
    • Linux
      • Learning guidelines
        • LPIC1 Notes
        • Linux Academy Notes
      • Install / Update Guides
        • Set up Raspberry PI
        • Update Debian 10 (buster) to Debian 11 (bullseye)
      • Increase disk size
      • umask
      • inodes
      • at jobs
      • yum
        • yum update vs yum upgrade
      • find
      • ssh
        • SSH returns: no matching host key type found. Their offer: ssh-rsa
        • Generate Public Key from Private Key
        • Run local bash scripts on remote server
      • crontab
        • Crontab 1st Sunday of every Month
        • Set crontab to execute after restart
      • vim
        • Use sed inside vim
      • networking
        • Check Port
      • fail2ban
      • bashrc
      • lvm
      • fallocate
        • Generate dummy file with actual size
      • openssl
        • Create Certificate via CNF file
        • OpenSSL cert conversion
    • Windows
      • Windows - Get App port by PID
      • Windows - Upgrade Windows build
      • Windows - Server
    • Android
      • Android - Motorola Unlock
      • Android - /E Project
    • PinePhone
      • PinePhone - Instructions for creating a PureOS image for PinePhone
  • Monitoring
    • Nagios
      • CPU threshold value calculation
    • New Relic
      • New Relic Flex Integration
      • NRQL Alerts examples
    • Zabbix
      • Zabbix Proxy not communicating with Windows Server
  • Microsoft Suite
    • Outlook
      • Change View
    • Excel
      • Excel Shortcuts
    • Windows Subset for Linux
      • WSL no internet connection
  • Networking
    • General Networking
      • IP Classes and Subnet Masks
      • Network CIDR Charts - /-es or IP Prefix
      • OSI Model Overview
      • Three Way Handshake & TCP Overview
    • F5
    • Authelia
      • What is Authelia
    • Nginx Proxy Manager
      • Nginx Proxy Manager - DuckDNS going down
    • Nmap
    • OpenWRT
      • Securing OpenWRT
      • OpenWRT - Read logs
      • OpenWRT - Adding DHCP Entry
      • OpenWRT - Wireguard
      • OpenWRT - Set up OpenVPN
      • OpenWRT - Internal DNS Service
      • OpenWRT - Set up new Wifi Interface
      • OpenWRT - Set up VLAN
      • OpenWRT - VPN Policy Routing
    • Pihole
      • Enabling HTTPS for your Pihole Web Interface
      • Edit Pihole DNS entries
    • RVS
      • RVS - Observer Modification
      • RVS - All Parameters
      • RVS - Adding a station
    • Wireguard
    • FTP
      • Connect to FTP anonymously
  • Pen Testing
    • CTF
      • CTF Links
  • Programming
    • Python
      • Classic Python
        • Python Cheatsheet
        • Python Shortcuts
        • Dunder Methods
        • hasattr(), getattr(), delattr()
        • Useful Exceptions
        • Dictionary
        • isinstance()
        • isdigit(), isdecimal(), isalpha()
        • return
        • Functions
        • Lists
        • ord(), chr()
        • squares, twos, odds
        • Bubble sort
        • append() and insert()
        • Bitwise operators
        • while, for & else
        • Arithmetic Operators
        • equal operators
        • Structure Projects
      • Modules
        • Webscraping
          • BeautifulSoup
        • PySimpleGui
          • Fast Crashcourse on PySimpleGui
        • os
        • python-docx
          • Generate DOCX file
        • psycopg2
          • PostgreSQL Connection
        • Pydantic Model vs SQLAlchemy Model
      • Frameworks
        • FastAPI
          • FastAPI Quick overview
          • Installing FastAPI and Dependencies
          • Starting FastAPI
          • Path Operations
          • Creating HTTP Operation paths
          • Send Data via Body of HTTP Request
          • Schema Validation with Pydantic
          • CRUD Operations
          • Storing in Array
          • Retrieve one individual entry
          • Changing response Status Codes
          • Deleting entries
          • Updating entries
          • API Documentation
          • Setup App Database & connect to database
          • FastAPI Response Model via Pydantic
          • Hashing passwords via FastAPI
          • Getting user by ID
          • FastAPI Routers
          • Router Prefix
          • Router Tags
          • JWT Token Basics
          • Login Process
          • Creating Token with OAuth2
          • OAuth2 PasswordRequestForm
          • Verify user is Logged In
          • Protecting Routes
          • Fetching User in Protected Routes
        • SQLAlchemy
          • What is an ORM
          • SQLAlchemy setup
          • Adding CreatedAt Column
          • CRUD via SQLAlchemy
          • Efficient way of passing params in SQLAlchemy
          • Creating Users Table via SQLAlchemy & FastAPI
      • Virtual Environments (venv)
    • General Programming Concepts
    • Interview Questions & Answers
      • General Programming Questions
      • Python Interview Questions Beginner
    • Courses
      • Python - PCAP-31-03 Course
        • Overview & Introduction
          • Exam Syllabus
          • Basics of variables
          • Basic Data Types
          • Basic Arithmetic in Python
          • Indexing and Slicing Strings
          • Basic String Methods
          • Format Method
          • Strings are Immutable
        • Lists, Tuples and Dictionaries
          • Lists
          • Accessing Elements in Nested Lists
          • Finding Index positions in Lists and counting duplicates
          • Tuples
          • Dictionaries
          • Comparison Operators
        • Functions and Variable Scope
          • Creating functions
          • *args and **kwargs
          • Basic Variable scope
          • Scope and Nested functions
        • Control Flow
          • If & Else Statements
          • Elif Statements
          • For Loops
          • Pass Statement in For Loops
          • While Loops
          • Looping and Unpacking with Dictionaries and Tuples
          • Range, Enumerate and Zip Functions
          • More Handy Functions and the Random Package
          • Accepting Input from User
        • Modules, Packages and OOP
          • Revising the Difference between Methods and Functions
          • Classes and Objects
          • Classes Attributes vs Object Attributes
          • Calling Python Code that is Saved in Another File
          • Inheritance and Polymorphism
          • Abstract Classes and Methods
          • Practical Application of OOP
          • Double Under (Dunder) Methods
          • Python Script Files
          • Python Files
          • Understanding the if __name__ == '__main__' Syntax
        • File IO and Exception Handling
          • Exception Handling
          • File IO
          • File IO with Exception Handling
          • OS Module
          • argv Command Line Arguments and the re Module
        • Misc Stuff and Q&A
    • IDE
      • Virtual Studio Code
        • Cheatsheet
    • Postman
      • Postman Overview
      • Create a GET HTTP request
      • HTTP Requests
      • Saving Postman requests
      • Environment Variables
  • Virtualization
    • Proxmox
      • Proxmox Cheatsheet
      • Proxmox Common Errors
      • Install Home Assistant in Proxmox via script
      • Create cloud-init template
      • Install guest-agent on new VM
      • Proxmox post install script
  • Webservers
    • Apache
      • Redirect 301 - Apache to index.html
    • Glassfish
      • Redirect 301 Glassfish
    • Tomcat
      • Useful tomcat files
  • Storage
    • NetApp
      • Netapp Overview
      • How to create symlinks
    • Nextcloud
      • Nextcloud Snap install and S3 Storage Bucket
      • Nextcloud Fail2Ban Regex
      • Set up OnlyOffice on Nextcloud
      • Set up Joplin and CalDav on Nextcloud
  • Software
    • Ansys
      • Ansys missing libraries
      • Ansys install
    • Jboss
      • Jboss process not working
Powered by GitBook
On this page
  • Currently our structure looks like this:
  • We will have to update to this:
  • We have an issue where we do not have access to the "app" object within our other files
  • The last step is to make the reference in our main.py file
  1. Programming
  2. Python
  3. Frameworks
  4. FastAPI

FastAPI Routers

PreviousGetting user by IDNextRouter Prefix

Last updated 2 years ago

  • It can become quite cluttered if you have all your routes in the main.py file

  • That's why the best approach is to split this into 2 files

    • One of them should contain all the CRUD operations

    • The other should contain all the user paths

  • This is not as simple as just moving the paths to a different file

  • This will be done by something called routers

Currently our structure looks like this:

main.py

from typing import List
from fastapi import Depends, FastAPI, Response, status, HTTPException

from sqlalchemy.orm import Session
from . import models, schemas, utils
from app.database import engine, get_db

models.Base.metadata.create_all(bind=engine)


app = FastAPI()

# while True:

#     try:
#         # Remote install
#         conn = psycopg.connect("host='postgres-01.lan' port='5432' dbname='fastapi' user='fastapi' password='Password1'")
        
#         # Local install
#         conn = psycopg.connect("host='localhost' port='5432' dbname='fastapi' user='postgres' password='Password1'")

#         cursor = conn.cursor()
#         print("Database connection successful")
#         break

#     except Exception as error:
#         print("Connecting to database failed")
#         print("Error: ", error)
#         time.sleep(2)


# Default Langing page
@app.get("/")
def root():
    return {"message": "Hello World"}


# Get all posts that are stored in DB/Memory (Depending on backend)
@app.get("/posts", response_model=List[schemas.PostResponse])
def get_posts(db: Session = Depends(get_db)):

    # Raw SQL
    # cursor.execute(""" SELECT * FROM posts """)
    # posts = cursor.fetchall()

    # SQLAlchemy (ORM)
    posts = db.query(models.Post).all()

    return posts


# Creates new post and returns said post
@app.post("/posts", status_code=status.HTTP_201_CREATED, response_model=schemas.PostResponse)
def create_posts(post: schemas.PostCreate, db: Session = Depends(get_db)):
    
    # Raw SQL
    # cursor.execute(""" INSERT INTO posts (title, content, published) VALUES (%s, %s, %s) RETURNING *""", (post.title, post.content, post.published))
    
    # new_post = cursor.fetchone()
    # conn.commit() # Required for committing the changes

    # SQLAlchemy

    new_post = models.Post(**post.dict())

    # Add post to database
    db.add(new_post)

    # Commit it to the database
    db.commit()

    # Returning - Basically refresh table and retrieve the post in order to get the
    db.refresh(new_post)

    return new_post


# Note that this is sigunlar to retrieve one single post
# Has {id} because the user needs to specify the id of the post

@app.get("/posts/{id}",response_model=schemas.PostResponse)

# 1. You would keep this as int to validate the number correctly so that the user does not pass an actual string
def get_post(id: int, db: Session = Depends(get_db)):

    # 2. You would convert it to a string in the query because it expects a string
    
    # Raw SQL
    # cursor.execute(""" SELECT * FROM posts WHERE id = %s """, (str(id),)) # Note you need to set it up as (id), and not (id) - Weird issue where you need to pass tuple in
    # post = cursor.fetchone()
    
    # SQLAlchemy
    post = db.query(models.Post).filter(models.Post.id == id).first()


    if not post:
        raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=f"post with id {id} was not found")

    return post

# Delete a post

@app.delete("/posts/{id}", status_code=status.HTTP_204_NO_CONTENT)
def delete_post(id: int, db: Session = Depends(get_db)):
    # Deleting posts
    # find the index in the array that requires the ID
    # my_posts.pop()

    # Raw SQL
    # cursor.execute(""" DELETE FROM posts * WHERE id = %s RETURNING *""", (str(id),))
    # deleted_post = cursor.fetchone()
    # conn.commit()


    # SQLAlchemy
    post = db.query(models.Post).filter(models.Post.id == id)



    if post.first() == None:
        raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=f"post with {id} does not exist")

    post.delete(synchronize_session=False)
    db.commit()

    return Response(status_code=status.HTTP_204_NO_CONTENT)


@app.put("/posts/{id}", response_model=schemas.PostResponse)
def update_posts(id: int, post_schema: schemas.PostCreate, db: Session = Depends(get_db)):

    # Raw SQL
    # cursor.execute(""" UPDATE posts SET title = %s, content = %s, published = %s WHERE id = %s RETURNING *""", (post.title, post.content, post.published, str(id)))
    # updated_post = cursor.fetchone()
    # conn.commit()

    # SQLAlchemy
    post_query = db.query(models.Post).filter(models.Post.id == id)

    post = post_query.first()

    # Raising a 404 if post index position of post is not found
    if post == None:
        raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=f"post with {id} does not exist")

    post_query.update(post_schema.dict(), synchronize_session=False)

    db.commit()

    return post_query.first()  # Return new post post


# Create USER
@app.post("/users", status_code=status.HTTP_201_CREATED, response_model=schemas.UserOut)
def create_user(user: schemas.UserCreate, db: Session = Depends(get_db)):

    # Hash the Password - user.password
    hashed_password = utils.hash(user.password)
    user.password = hashed_password

    new_user = models.User(**user.dict())

    db.add(new_user)
    db.commit()
    db.refresh(new_user)

    return new_user


# Get USER by ID
@app.get("/users/{id}", response_model=schemas.UserOut)
def get_user(id: int, db: Session = Depends(get_db)):

    user = db.query(models.User).filter(models.User.id == id).first()

    if not user:
        raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=f"User with id: {id} does not exist")

    return user

We will have to update to this:

  • Fist we will add a folder called routers

  • Within this we will create 2 files:

    • post.py

    • users.py

  • Now we will have to move all the routes from main.py

    • The ones for posts in post.py

    • The ones for users in users.py

Note: This will create a lot of issues initially as we will have to do all the imports for the files. These were initially imported in main.py

users.py

from fastapi import Depends, FastAPI, Response, status, HTTPException
from .. import models, schemas, utils
from sqlalchemy.orm import Session
from app.database import get_db

# Create USER
@app.post("/users", status_code=status.HTTP_201_CREATED, response_model=schemas.UserOut)
def create_user(user: schemas.UserCreate, db: Session = Depends(get_db)):

    # Hash the Password - user.password
    hashed_password = utils.hash(user.password)
    user.password = hashed_password

    new_user = models.User(**user.dict())

    db.add(new_user)
    db.commit()
    db.refresh(new_user)

    return new_user


# Get USER by ID
@app.get("/users/{id}", response_model=schemas.UserOut)
def get_user(id: int, db: Session = Depends(get_db)):

    user = db.query(models.User).filter(models.User.id == id).first()

    if not user:
        raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=f"User with id: {id} does not exist")

    return user

post.py

from fastapi import Depends, FastAPI, Response, status, HTTPException
from .. import models, schemas
from sqlalchemy.orm import Session
from app.database import get_db
from typing import List


# Get all posts that are stored in DB/Memory (Depending on backend)
@app.get("/posts", response_model=List[schemas.PostResponse])
def get_posts(db: Session = Depends(get_db)):

    # Raw SQL
    # cursor.execute(""" SELECT * FROM posts """)
    # posts = cursor.fetchall()

    # SQLAlchemy (ORM)
    posts = db.query(models.Post).all()

    return posts


# Creates new post and returns said post
@app.post("/posts", status_code=status.HTTP_201_CREATED, response_model=schemas.PostResponse)
def create_posts(post: schemas.PostCreate, db: Session = Depends(get_db)):
    
    # Raw SQL
    # cursor.execute(""" INSERT INTO posts (title, content, published) VALUES (%s, %s, %s) RETURNING *""", (post.title, post.content, post.published))
    
    # new_post = cursor.fetchone()
    # conn.commit() # Required for committing the changes

    # SQLAlchemy

    new_post = models.Post(**post.dict())

    # Add post to database
    db.add(new_post)

    # Commit it to the database
    db.commit()

    # Returning - Basically refresh table and retrieve the post in order to get the
    db.refresh(new_post)

    return new_post


# Note that this is sigunlar to retrieve one single post
# Has {id} because the user needs to specify the id of the post

@app.get("/posts/{id}",response_model=schemas.PostResponse)

# 1. You would keep this as int to validate the number correctly so that the user does not pass an actual string
def get_post(id: int, db: Session = Depends(get_db)):

    # 2. You would convert it to a string in the query because it expects a string
    
    # Raw SQL
    # cursor.execute(""" SELECT * FROM posts WHERE id = %s """, (str(id),)) # Note you need to set it up as (id), and not (id) - Weird issue where you need to pass tuple in
    # post = cursor.fetchone()
    
    # SQLAlchemy
    post = db.query(models.Post).filter(models.Post.id == id).first()


    if not post:
        raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=f"post with id {id} was not found")

    return post

# Delete a post

@app.delete("/posts/{id}", status_code=status.HTTP_204_NO_CONTENT)
def delete_post(id: int, db: Session = Depends(get_db)):
    # Deleting posts
    # find the index in the array that requires the ID
    # my_posts.pop()

    # Raw SQL
    # cursor.execute(""" DELETE FROM posts * WHERE id = %s RETURNING *""", (str(id),))
    # deleted_post = cursor.fetchone()
    # conn.commit()


    # SQLAlchemy
    post = db.query(models.Post).filter(models.Post.id == id)



    if post.first() == None:
        raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=f"post with {id} does not exist")

    post.delete(synchronize_session=False)
    db.commit()

    return Response(status_code=status.HTTP_204_NO_CONTENT)


@app.put("/posts/{id}", response_model=schemas.PostResponse)
def update_posts(id: int, post_schema: schemas.PostCreate, db: Session = Depends(get_db)):

    # Raw SQL
    # cursor.execute(""" UPDATE posts SET title = %s, content = %s, published = %s WHERE id = %s RETURNING *""", (post.title, post.content, post.published, str(id)))
    # updated_post = cursor.fetchone()
    # conn.commit()

    # SQLAlchemy
    post_query = db.query(models.Post).filter(models.Post.id == id)

    post = post_query.first()

    # Raising a 404 if post index position of post is not found
    if post == None:
        raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=f"post with {id} does not exist")

    post_query.update(post_schema.dict(), synchronize_session=False)

    db.commit()

    return post_query.first()  # Return new post post

We have an issue where we do not have access to the "app" object within our other files

  • Fist instinct would be to import the app object but that is not exactly correct

  • We will have to make use of the routers

  • We will have to import APIRoutersfirst

from fastapi import Depends, FastAPI, Response, status, HTTPException, APIRouter

router = APIRouter
  • Next we will create the routerobject

  • After which we will replace the appobject with the routerobject

from fastapi import Depends, FastAPI, Response, status, HTTPException, APIRouter
from .. import models, schemas, utils
from sqlalchemy.orm import Session
from app.database import get_db

router = APIRouter()

# Create USER
@router.post("/users", status_code=status.HTTP_201_CREATED, response_model=schemas.UserOut)
def create_user(user: schemas.UserCreate, db: Session = Depends(get_db)):

    # Hash the Password - user.password
    hashed_password = utils.hash(user.password)
    user.password = hashed_password

    new_user = models.User(**user.dict())

    db.add(new_user)
    db.commit()
    db.refresh(new_user)

    return new_user


# Get USER by ID
@router.get("/users/{id}", response_model=schemas.UserOut)
def get_user(id: int, db: Session = Depends(get_db)):

    user = db.query(models.User).filter(models.User.id == id).first()

    if not user:
        raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=f"User with id: {id} does not exist")

    return user
  • It's more or less the same for the post.pyfile as well

from fastapi import Depends, FastAPI, Response, status, HTTPException, APIRouter
from .. import models, schemas
from sqlalchemy.orm import Session
from app.database import get_db
from typing import List


router = APIRouter()

# Get all posts that are stored in DB/Memory (Depending on backend)
@router.get("/posts", response_model=List[schemas.PostResponse])
def get_posts(db: Session = Depends(get_db)):

    # Raw SQL
    # cursor.execute(""" SELECT * FROM posts """)
    # posts = cursor.fetchall()

    # SQLAlchemy (ORM)
    posts = db.query(models.Post).all()

    return posts


# Creates new post and returns said post
@router.post("/posts", status_code=status.HTTP_201_CREATED, response_model=schemas.PostResponse)
def create_posts(post: schemas.PostCreate, db: Session = Depends(get_db)):
    
    # Raw SQL
    # cursor.execute(""" INSERT INTO posts (title, content, published) VALUES (%s, %s, %s) RETURNING *""", (post.title, post.content, post.published))
    
    # new_post = cursor.fetchone()
    # conn.commit() # Required for committing the changes

    # SQLAlchemy

    new_post = models.Post(**post.dict())

    # Add post to database
    db.add(new_post)

    # Commit it to the database
    db.commit()

    # Returning - Basically refresh table and retrieve the post in order to get the
    db.refresh(new_post)

    return new_post


# Note that this is sigunlar to retrieve one single post
# Has {id} because the user needs to specify the id of the post

@router.get("/posts/{id}",response_model=schemas.PostResponse)

# 1. You would keep this as int to validate the number correctly so that the user does not pass an actual string
def get_post(id: int, db: Session = Depends(get_db)):

    # 2. You would convert it to a string in the query because it expects a string
    
    # Raw SQL
    # cursor.execute(""" SELECT * FROM posts WHERE id = %s """, (str(id),)) # Note you need to set it up as (id), and not (id) - Weird issue where you need to pass tuple in
    # post = cursor.fetchone()
    
    # SQLAlchemy
    post = db.query(models.Post).filter(models.Post.id == id).first()


    if not post:
        raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=f"post with id {id} was not found")

    return post

# Delete a post

@router.delete("/posts/{id}", status_code=status.HTTP_204_NO_CONTENT)
def delete_post(id: int, db: Session = Depends(get_db)):
    # Deleting posts
    # find the index in the array that requires the ID
    # my_posts.pop()

    # Raw SQL
    # cursor.execute(""" DELETE FROM posts * WHERE id = %s RETURNING *""", (str(id),))
    # deleted_post = cursor.fetchone()
    # conn.commit()


    # SQLAlchemy
    post = db.query(models.Post).filter(models.Post.id == id)



    if post.first() == None:
        raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=f"post with {id} does not exist")

    post.delete(synchronize_session=False)
    db.commit()

    return Response(status_code=status.HTTP_204_NO_CONTENT)


@router.put("/posts/{id}", response_model=schemas.PostResponse)
def update_posts(id: int, post_schema: schemas.PostCreate, db: Session = Depends(get_db)):

    # Raw SQL
    # cursor.execute(""" UPDATE posts SET title = %s, content = %s, published = %s WHERE id = %s RETURNING *""", (post.title, post.content, post.published, str(id)))
    # updated_post = cursor.fetchone()
    # conn.commit()

    # SQLAlchemy
    post_query = db.query(models.Post).filter(models.Post.id == id)

    post = post_query.first()

    # Raising a 404 if post index position of post is not found
    if post == None:
        raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=f"post with {id} does not exist")

    post_query.update(post_schema.dict(), synchronize_session=False)

    db.commit()

    return post_query.first()  # Return new post post

The last step is to make the reference in our main.py file

  • Otherwise the API will not work

  • We will import the post and user from the routers folder

from fastapi import Depends, FastAPI, Response, status, HTTPException
from . import models
from app.database import engine

from .routers import post, user

models.Base.metadata.create_all(bind=engine)


app = FastAPI()



app.include_router(post.router)
app.include_router(user.router)

# Default Langing page
@app.get("/")
def root():
    return {"message": "Hello World"}
  • Then we will use the app object to import the post and user routers

How does this work?

app.include_router(post.router)
app.include_router(user.router)
  • If we get a request, the above 2 lines will check the post and user files respectively and see if we have a match within those

  • If a request hits one of the routes in the files, it will work as it did previously with everything in main.py