Creating an API with python: Part 4: DELETE Endpoints

In my previous post, Creating an API with python: Part 3: POST Endpoints, I added three POST endpoints to the FastAPI API. In this post, I’ll add three DELETE endpoints which will remove data from the database.

Prerequisites

  1. Creating an API with python: Part 1: GET Endpoints
  2. Creating an API with python: Part 2: MariaDB Database
  3. Creating an API with python: Part 3: POST Endpoints

Step 1: Update manager.py

Update the manager.py file with three new functions for deleting each type of API object: link, tag, taglink.

  1. Change to the code directory (~/vboxshare/fastapi should be replaced with the path to your FastAPI python code):
    $ cd ~/vboxshare/fastapi
    
  2. Change to the manager directory.
    $ cd manager
    
  3. Open the manager.py file and append it with the following functions:
    def delete_link(db: Session, link_id: str):
        db_link = get_link(db, link_id=link_id)
        if not db_link:
            raise HTTPException(status_code=404, detail=f"Link with link_id {link_id} not found")
        delete_taglinks(db, link_id=link_id)
        db.delete(db_link)
        db.commit()
        return "OK"
    
    
    def delete_tag(db: Session, tag_id: str):
        db_tag = get_tag(db, tag_id=tag_id)
        if not db_tag:
            raise HTTPException(status_code=404, detail=f"Tag with tag_id {tag_id} not found")
        delete_taglinks(db, tag_id=tag_id)
        db.delete(db_tag)
        db.commit()
        return "OK"
    
    
    def delete_taglinks(db: Session, tag_id: Optional[str] = None, link_id: Optional[str] = None):
        db_taglinks = get_taglinks(db, tag_id=tag_id, link_id=link_id)
        for db_taglink in db_taglinks:
            db.delete(db_taglink)
        db.commit()
        return "OK"
    

Step 2: Update main.py

Update main.py with three new DELETE endpoint functions: delete_link, delete_tag and delete_taglink.

  1. Change directory to the top level directory:
    $ cd ../
    
  2. Append main.py with these three functions:
    # Delete link by link_id
    @app.delete("/link/{link_id}")
    async def delete_link(link_id: str, db: Session = Depends(get_db)):
        return manager.delete_link(db, link_id)
    
    
    # Delete tag by tag_id
    @app.delete("/tag/{tag_id}")
    async def delete_tag(tag_id: str, db: Session = Depends(get_db)):
        return manager.delete_tag(db, tag_id)
    
    
    # Delete taglinks by query params
    @app.delete("/taglink/")
    async def delete_taglinks(link_id: Optional[str] = None, tag_id: Optional[str] = None,  db: Session = Depends(get_db)):
        if link_id is None and tag_id is None:
            raise HTTPException(status_code=422, detail="One or both of tag_id and link_id must be specified")
        return manager.delete_taglinks(db, tag_id, link_id)
    

Step 3: Update Test Script

Now we need to update the test script to test the POST endpoints, retrieve the responses, test the GET endpoints with the data retrieved and finally test the DELETE endpoints.

  1. Open the file test.sh, and replace the code with the following:
    IP=$1
    BASEURL=http://$IP:8000
    
    echo "=========================="
    echo "Test POST /link link=https://www.test1.com, tag=test1"
    resp=$(curl -X "POST" -H "Content-Type: application/json" -d "{\"link\": \"https://www.test1.com\", \"tag\": \"test1\"}" $BASEURL/link/)
    echo $resp
    link_id1=$(echo $resp | jq -r '.link_id')
    echo $link_id1
    
    echo "=========================="
    echo "Test GET /tag tag=test1"
    resp=$(curl -X "GET" -H "Content-Type: application/json" $BASEURL/tag/?tag=test1)
    echo $resp
    tag_id1=$(echo $resp | jq -r '.[0].tag_id')
    echo $tag_id1
    
    echo "=========================="
    echo "Test POST /tag tag=test2"
    resp=$(curl -X "POST" -H "Content-Type: application/json" -d "{\"tag\": \"test2\"}" $BASEURL/tag/)
    echo $resp
    tag_id2=$(echo $resp | jq -r '.tag_id')
    echo $tag_id2
    
    echo "=========================="
    echo "Test POST /tag tag=test3"
    resp=$(curl -X "POST" -H "Content-Type: application/json" -d "{\"tag\": \"test3\"}" $BASEURL/tag/)
    echo $resp
    tag_id3=$(echo $resp | jq -r '.tag_id')
    echo $tag_id3
    
    echo "=========================="
    echo "Test POST /link link=https://www.test2.com, tag=test2"
    resp=$(curl -X "POST" -H "Content-Type: application/json" -d "{\"link\": \"https://www.test2.com\", \"tag\": \"test2\"}" $BASEURL/link/)
    echo $resp
    link_id2=$(echo $resp | jq -r '.link_id')
    echo $link_id2
    
    echo "=========================="
    echo "Test POST /link link=https://www.test2.com, tag_id=$tag_id1"
    resp=$(curl -X "POST" -H "Content-Type: application/json" -d "{\"link\": \"https://www.test2.com\", \"tag_id\": \"$tag_id1\"}" $BASEURL/link/)
    echo $resp
    link_id2=$(echo $resp | jq -r '.link_id')
    echo $link_id2
    
    echo "=========================="
    echo "Test POST /link link=https://www.test3.com, tag_id=$tag_id3"
    resp=$(curl -X "POST" -H "Content-Type: application/json" -d "{\"link\": \"https://www.test3.com\", \"tag_id\": \"$tag_id3\"}" $BASEURL/link/)
    echo $resp
    link_id3=$(echo $resp | jq -r '.link_id')
    echo $link_id3
    
    echo "=========================="
    echo "Test POST /link link=https://www.test2.com, tag_id=invalid. Expect 404 response: Tag with tag_id invalid not found"
    resp=$(curl -X "POST" -H "Content-Type: application/json" -d "{\"link\": \"https://www.test2.com\", \"tag_id\": \"invalid\"}" $BASEURL/link/)
    echo $resp
    
    echo "=========================="
    echo "Test GET /link"
    resp=$(curl -X "GET" -H "Content-Type: application/json" $BASEURL/link/)
    echo $resp
    
    echo "=========================="
    echo "Test GET /link tag_id=$tag_id1"
    resp=$(curl -X "GET" -H "Content-Type: application/json" $BASEURL/link/?tag_id=$tag_id1)
    echo $resp
    
    echo "=========================="
    echo "Test GET /link tag=test1"
    resp=$(curl -X "GET" -H "Content-Type: application/json" $BASEURL/link/?tag=test1)
    echo $resp
    
    echo "=========================="
    echo "Test GET /link link_id=$link_id1"
    resp=$(curl -X "GET" -H "Content-Type: application/json" $BASEURL/link/$link_id1)
    echo $resp
    
    echo "=========================="
    echo "Test GET /tag tag_id=$tag_id1"
    resp=$(curl -X "GET" -H "Content-Type: application/json" $BASEURL/tag/$tag_id1)
    echo $resp
    
    echo "=========================="
    echo "Test GET /tag"
    resp=$(curl -X "GET" -H "Content-Type: application/json" $BASEURL/tag/)
    echo $resp
    
    echo "=========================="
    echo "Test POST /taglink tag_id=$tag_id2, link_id=$link_id1"
    resp=$(curl -X "POST" -H "Content-Type: application/json" -d "{\"link_id\": \"$link_id1\", \"tag_id\": \"$tag_id2\"}" $BASEURL/taglink/)
    echo $resp
    
    echo "=========================="
    echo "Test POST /taglink tag_id=$tag_id2, link_id=$link_id3"
    resp=$(curl -X "POST" -H "Content-Type: application/json" -d "{\"link_id\": \"$link_id3\", \"tag_id\": \"$tag_id2\"}" $BASEURL/taglink/)
    echo $resp
    
    echo "=========================="
    echo "Test POST /taglink tag_id=invalid, link_id=$link_id1. Expect 422 response: Tag with tag_id invalid not found"
    resp=$(curl -X "POST" -H "Content-Type: application/json" -d "{\"link_id\": \"$link_id1\", \"tag_id\": \"invalid\"}" $BASEURL/taglink/)
    echo $resp
    
    echo "=========================="
    echo "Test POST /taglink tag_id=$tag_id1, link_id=invalid. Expect 422 response: Link with link_id invalid not found"
    resp=$(curl -X "POST" -H "Content-Type: application/json" -d "{\"link_id\": \"invalid\", \"tag_id\": \"$tag_id1\"}" $BASEURL/taglink/)
    echo $resp
    
    echo "=========================="
    echo "Test POST /taglink tag_id=$tag_id1, link_id=$link_id1. Expect 409 response: TagLink with tag_id $tag_id1 and link_id $link_id1 exists"
    resp=$(curl -X "POST" -H "Content-Type: application/json" -d "{\"link_id\": \"$link_id1\", \"tag_id\": \"$tag_id1\"}" $BASEURL/taglink/)
    echo $resp
    
    echo "=========================="
    echo "Test GET /taglink"
    resp=$(curl -X "GET" -H "Content-Type: application/json" $BASEURL/taglink/)
    echo $resp
    
    echo "=========================="
    echo "Test GET /taglink link_id=$link_id1"
    resp=$(curl -X "GET" -H "Content-Type: application/json" $BASEURL/taglink/?link_id=$link_id1)
    echo $resp
    
    echo "=========================="
    echo "Test GET /taglink tag_id=$tag_id1"
    resp=$(curl -X "GET" -H "Content-Type: application/json" $BASEURL/taglink/?tag_id=$tag_id1)
    echo $resp
    
    echo "=========================="
    echo "Test GET /taglink tag_id=$tag_id1 link_id=$link_id1"
    resp=$(curl -X "GET" -H "Content-Type: application/json" $BASEURL/taglink/?tag_id=$tag_id1&link_id=$link_id1)
    echo $resp
    
    echo "=========================="
    echo "Test DELETE /taglink. Expect 422 response: One or both of tag_id or link_id must be specified"
    resp=$(curl -X "DELETE" -H "Content-Type: application/json" $BASEURL/taglink/)
    echo $resp
    
    echo "=========================="
    echo "Test DELETE /taglink tag_id=$tag_id3"
    resp=$(curl -X "DELETE" -H "Content-Type: application/json" $BASEURL/taglink/?tag_id=$tag_id3)
    echo $resp
    
    echo "=========================="
    echo "Test DELETE /taglink link_id=$link_id3"
    resp=$(curl -X "DELETE" -H "Content-Type: application/json" $BASEURL/taglink/?link_id=$link_id3)
    echo $resp
    
    echo "=========================="
    echo "Test DELETE /tag tag_id=$tag_id1"
    resp=$(curl -X "DELETE" -H "Content-Type: application/json" $BASEURL/tag/$tag_id1)
    echo $resp
    
    echo "=========================="
    echo "Test DELETE /link link_id=$link_id1"
    resp=$(curl -X "DELETE" -H "Content-Type: application/json" $BASEURL/link/$link_id1)
    echo $resp
    
    echo "=========================="
    echo "Test DELETE /tag tag_id=$tag_id2"
    resp=$(curl -X "DELETE" -H "Content-Type: application/json" $BASEURL/tag/$tag_id2)
    echo $resp
    
    echo "=========================="
    echo "Test DELETE /link link_id=$link_id2"
    resp=$(curl -X "DELETE" -H "Content-Type: application/json" $BASEURL/link/$link_id2)
    echo $resp
    
    echo "=========================="
    echo "Test DELETE /tag tag_id=$tag_id3"
    resp=$(curl -X "DELETE" -H "Content-Type: application/json" $BASEURL/tag/$tag_id3)
    echo $resp
    
    echo "=========================="
    echo "Test DELETE /link link_id=$link_id3"
    resp=$(curl -X "DELETE" -H "Content-Type: application/json" $BASEURL/link/$link_id3)
    echo $resp
    
    echo "=========================="
    echo "Test DELETE /tag tag_id=blah. Expect 404 response: Tag with tag_id blah not found"
    resp=$(curl -X "DELETE" -H "Content-Type: application/json" $BASEURL/tag/blah)
    echo $resp
    
    echo "=========================="
    echo "Test DELETE /link link_id=blah. Expect 404 response: Link with link_id blah not found"
    resp=$(curl -X "DELETE" -H "Content-Type: application/json" $BASEURL/link/blah)
    echo $resp
    
  2. Make sure the test script is executable:
    $ chmod ugo+x test.sh
    

Step 4: Run the test script

  1. Run the test script, replacing YOUR_IP with the IP of your API host server (or 127.0.0.1 if running the test script from the machine that is hosting the API):
    $ ./test.sh YOUR_IP
    
    If all goes well, you should see an output of json responses to each request, with no errors, other than those expected in the test descriptions.
  2. If you want to run the test script again, you can now do so without needing to delete the test data from the database, as the test script does this in the process of testing the DELETE endpoints.

Conclusion

You should now have a FastAPI API running with five GET endpoints, three POST endpoints and three DELETE endpoints that call into a MariaDB database to insert, retrieve and delete data.

To add OAuth2 Authentication, see my follow-on post, Creating an API with python: Part 5: Authentication.

Thanks for reading!