Skip to content

circobit/trivia-api

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

27 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Trivia API

Python Version Flask Coverage

A backend REST API built with Flask and PostgreSQL, developed as part of Udacity’s Backend Developer Nanodegree.
It powers a trivia game application, handling questions, categories, search, and quiz logic.

Note: The frontend interface was provided by Udacity and is included only to interact with and test the API.

App Demo

Table of Contents

🧩 Project Overview

The Trivia API enables users to:

  • View all available trivia categories.
  • Browse paginated trivia questions.
  • Create and delete questions.
  • Search questions by keyword.
  • Play a quiz game that returns non-repeated random questions from a selected category.

The backend was built using Flask and SQLAlchemy, following Test-Driven Development (TDD) principles, with unit tests ensuring correctness and reliability.

⚙️ Features Implemented

This project involved building the entire backend RESTful API from a partially completed Flask application. The following features were implemented and fully tested using a Test-Driven Development (TDD) approach:

  • Endpoint for All Questions and Categories: Implemented endpoints to fetch all available question categories and a paginated list of all questions.
  • Question Management: Developed full Create and Delete functionality for questions in the database, allowing users to add new trivia and remove existing entries.
  • Category-Specific Views: Created an endpoint to retrieve all questions belonging to a single, user-selected category.
  • Powerful Search: Built a search endpoint that allows users to find questions based on a case-insensitive, partial text match.
  • Interactive Quiz Logic: Designed and implemented the core quiz functionality with a stateful endpoint that serves random, non-repeating questions from either "All" categories or a specific one.
  • Error Handling: Implemented comprehensive error handlers for common status codes (404, 422, 405) to provide clear, consistent JSON error responses.

🚀 Getting Started

Prerequisites

  • Python 3.12.12 (fully tested and recommended)
  • Pip
  • Node.js & NPM
  • PostgreSQL

Backend Installation & Setup

  1. Clone the Repository:

    git clone git@github.com:circobit/trivia-api.git
    cd trivia-api
  2. Navigate to the Backend Directory:

    cd backend
  3. Create and Activate a Virtual Environment:

    python3 -m venv venv
    source venv/bin/activate
  4. Install Dependencies:

    pip install -r requirements.txt
  5. Set up the Database: Create the main database and load the starter data.

    dropdb trivia
    createdb trivia
    psql trivia < trivia.psql

Frontend (Provided)

The frontend application was provided by Udacity for interacting with and testing the API.
To use it locally:

  1. Navigate to the Frontend Directory:

    cd frontend
  2. Install Dependencies:

    npm install

🖥️ Running the Application

Both the backend and frontend servers must be running concurrently.

Running the Backend Server

From the backend/ directory:

export FLASK_APP=flaskr
export FLASK_ENV=development
flask run

The backend will be running at http://127.0.0.1:5000/

Running the Frontend Application

From the frontend/ directory:

export NODE_OPTIONS=--openssl-legacy-provider && npm start

The frontend will be running at http://127.0.0.1:3000/

🧪 Running Tests

This project includes tests located in backend/test_flaskr.py.

Create the Test Database

Before running tests, create a PostgreSQL database for testing:

psql -U postgres
CREATE DATABASE trivia_test;
\q

If needed, update your credentials in test_flaskr.py:

self.database_user = "your_username"
self.database_password = "your_password"

To run the backend test suite, navigate to the backend/ directory and run:

pytest -v

Test Coverage

To run the test coverage report, go to the backend/ directory and run:

pytest --cov=flaskr --cov-report=term-missing

🔍 API Reference

Method Endpoint Description
GET /categories Retrieve all categories
GET /questions?page=<n> Paginated list of questions
POST /questions Add a new question
DELETE /questions/<id> Delete a question
POST /questions/search Search questions by keyword
GET /categories/<id>/questions Get questions in a category
POST /quizzes Retrieve random quiz question

Error Handling

Errors are returned in a consistent JSON format:

{
    "success": false,
    "error": 404,
    "message": "resource not found"
}

The API supports 400, 404, 405, and 422 error codes.

GET /categories

  • Returns an object containing all available categories.
  • cURL Example: curl http://127.0.0.1:5000/categories
  • Response Body:
{
    "categories": {
        "1": "Science",
        "2": "Art",
        "3": "Geography",
        "4": "History",
        "5": "Entertainment",
        "6": "Sports"
    },
    "success": true
}

GET /questions?page=<integer>

  • Returns a paginated list of questions (10 per page), a list of all categories, and the total number of questions.
  • cURL Example: curl http://127.0.0.1:5000/questions?page=1
  • Response Body:
{
    "categories": { ... },
    "current_category": "All",
    "questions": [
        {
            "answer": "Tom Cruise",
            "category": 5,
            "difficulty": 4,
            "id": 4,
            "question": "What actor did author Anne Rice first denounce, then praise in the role of her beloved Lestat?"
        }
    ],
    "success": true,
    "total_questions": 19
}

DELETE /questions/<int:question_id>

  • Deletes the question with the given ID.
  • curl Example:
curl -X DELETE http://127.0.0.1:5000/questions/5
  • Response Body:
{
    "success": true
}

POST /questions

  • Creates a new question.
  • curl Example:
curl [http://127.0.0.1:5000/questions](http://127.0.0.1:5000/questions) -X POST -H "Content-Type: application/json" -d '{"question":"Who was the first man on the moon?","answer":"Neil Armstrong","difficulty":1,"category":"1"}'
  • Request Body:
{
    "question": "Who was the first man on the moon?",
    "answer": "Neil Armstrong",
    "difficulty": 1,
    "category": "1"
}
  • Response body:
{
    "success": true,
    "created": 23,
    "total_questions": 20
}

POST /questions/search

  • Returns questions that contain the given search term (case-insensitive).
  • curl Example:
curl [http://127.0.0.1:5000/questions/search](http://127.0.0.1:5000/questions/search) -X POST -H "Content-Type: application/json" -d '{"searchTerm":"who"}'
  • Request Body:
{
    "searchTerm": "who"
}
  • Response Body:
{
    "current_category": null,
    "questions": [
        {
            "answer": "Muhammad Ali",
            "category": 4,
            "difficulty": 1,
            "id": 9,
            "question": "Whose autobiography is entitled 'The Greatest: My Own Story'?"
        }
    ],
    "success": true,
    "total_questions": 1
}

GET /categories/<int:category_id>/questions

  • Returns all questions for a given category.
  • curl Example:
curl http://127.0.0.1:5000/categories/1/questions
  • Response Body:
{
    "current_category": 1,
    "questions": [
        {
            "answer": "The Liver",
            "category": "1",
            "difficulty": 4,
            "id": 20,
            "question": "What is the heaviest organ in the human body?"
        }
    ],
    "success": true,
    "total_questions": 3
}

POST /quizzes

  • Returns a random, unasked question to continue a quiz game.
  • curl Example:
curl [http://127.0.0.1:5000/quizzes](http://127.0.0.1:5000/quizzes) -X POST -H "Content-Type: application/json" -d '{"previous_questions":[20, 21],"quiz_category":{"id":"1","type":"Science"}}'
  • Request body:
{
    "previous_questions": [20, 21],
    "quiz_category": {
        "id": "1",
        "type": "Science"
    }
}
  • Response body with question
{
    "success": true,
    "question": {
        "answer": "Blood",
        "category": "1",
        "difficulty": 4,
        "id": 22,
        "question": "Hematology is a branch of medicine involving the study of what?"
    }
}
  • Response Body (end of quiz):
{
    "success": true,
    "question": null
}

🔧 Current Status

The backend currently runs locally using Flask’s development server and connects to a PostgreSQL instance. It has been fully upgraded and tested with Python 3.12.12, achieving 97% code coverage with all tests passing.

The project currently validates CRUD, search, and quiz endpoints through automated unit tests. Future improvements will focus on applying DevOps and SRE best practices to make the service production-ready.

🗺️ Roadmap

  • Upgrade and test the project with Python 3.12
  • Replace hardcoded database credentials with environment variables
  • Add Dockerfile and docker-compose configuration
  • Introduce /healthz and /readyz endpoints
  • Expose Prometheus-compatible metrics
  • Implement GitHub Actions for automated testing and builds
  • Prepare Helm chart for Kubernetes deployment

🧱 Python 3.12 Upgrade Notes

This project was upgraded from Python 3.9 to Python 3.12.12.
Key updates:

  • Updated dependencies for Python 3.12 compatibility.
  • Migrated to pytest 8.x and Flask 3.0 with no breaking changes.
  • Verified functionality and database migrations through the test suite.
  • Achieved increased test coverage from 93% → 97%.

👤 Author

Cristian Cevasco
Site Reliability Engineer
GitHubLinkedIn

📄 License

This project is licensed under the MIT License.
You are free to use, modify, and distribute this software in accordance with the terms of the license.

About

No description, website, or topics provided.

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors