A REST API for managing audio files and their metadata. Supports creating, reading, updating, and deleting tracks, along with tagging and search functionality.
- Flask (Python web framework)
- PostgreSQL (relational database)
- SQLAlchemy (ORM)
- pytest (testing)
- CRUD operations for audio tracks
- RESTful API design
- Tag management (many-to-many relationships)
- Search by title, artist, and tags
- Pagination for large datasets
- Python 3.x
- PostgreSQL installed locally
-
PostgreSQL
-
Install PostgreSQL (if not already installed)
- macOS:
brew install postgresql - Ubuntu/Debian:
sudo apt-get install postgresql - Arch Linux:
sudo pacman -S postgresql - Windows: Download from postgresql.org
- macOS:
-
Start PostgreSQL service
- macOS:
brew services start postgresql - Linux:
sudo service postgresql start - Windows: PostgreSQL should start automatically
- macOS:
-
Create the database
psql -U postgres CREATE DATABASE sound_library; \q
-
Set up authentication
- Create a file named sqlalchemy_password.txt in the project root
- Add your PostgreSQL password on the first line
- Add this file to .gitignore (security!)
-
-
Python Environment Setup
- Create virtual environment
python -m venv venv source venv/Scripts/activate - Install the dependencies
pip install -r requirements.txt
- Run the application
The API will be available at http://localhost:5000
python -m src.app
- Create virtual environment
http://localhost:5000
POST /api/tracks
Creates a new track in the database.
Request Body:
{
"title": "Song Name",
"artist_name": "Artist Name",
"duration_seconds": 240,
"file_path": "/path/to/file.mp3"
}Example:
curl -X POST http://localhost:5000/api/tracks \
-H "Content-Type: application/json" \
-d '{"title":"Beware","artist_name":"Death Grips","duration_seconds":352,"file_path":"/music/beware.mp3"}'Response: 201 Created
{
"id": 1,
"title": "Beware",
"artist_name": "Death Grips",
"duration_seconds": 352,
"file_path": "/music/beware.mp3"
}PUT /api/tracks/:id
Edits a track in the database.
Example:
curl -X PUT http://localhost:5000/api/tracks/1 \
-H "Content-Type: application/json" \
-d '{"title":"Known for it", "file_path": "/music/knownforit.mp3", "duration_seconds": 253, "tags": ["experimental", "hip-hop"]}'Response: 200 Success
{
"id": 1,
"title": "Known for it",
"artist_name": "Death Grips",
"duration_seconds": 253,
"file_path": "/music/knownforit.mp3",
"tags": ["experimental", "hip-hop"]
}Response: 404 error
{
"error": "Track not found"
}DELETE /api/tracks/:id
Delete a track in the database.
Example:
curl -X DELETE http://localhost:5000/api/tracks/1 \
-H "Content-Type: application/json" \Response: 200 Success
{
"Success": "Track 1: Known for it deleted"
}Response: 404 error
{
"error": "Track not found"
}GET /api/tracks/:id
Get a track from the database.
Example:
curl -X GET http://localhost:5000/api/tracks/1 \
-H "Content-Type: application/json" \Response: 200 Success
{
"id": 1,
"title": "Known for it",
"artist_name": "Death Grips",
"duration_seconds": 352,
"file_path": "/music/knownforit.mp3",
"tags": ["experimental", "hip-hop"]
}Response: 404 error
{
"error": "Track not found"
}GET /api/tracks
Get all tracks from the database.
Example:
curl -X GET http://localhost:5000/api/tracks \
-H "Content-Type: application/json" \Response: 200 Success
[
{
"id": 1,
"title": "Known for it",
"artist_name": "Death Grips",
"duration_seconds": 352,
"file_path": "/music/knownforit.mp3",
"tags": ["experimental", "hip-hop"]
},
{
"id": 2,
"title": "Get Got",
"artist_name": "Death Grips",
"duration_seconds": 191,
"file_path": "/music/getgot.mp3",
"tags": ["experimental", "hip-hop"]
}
]GET /api/tracks/search
Search for tracks by title, artist name, or tags.
Query Parameters:
q(required) - Search querylimit(optional, default: 20) - Number of results per pageoffset(optional, default: 0) - Starting position
Example:
curl -X GET "http://localhost:5000/api/tracks/search?q=death&limit=10" \
-H "Content-Type: application/json"Response: 200 OK
{
"tracks": [
{
"id": 1,
"title": "Beware",
"artist_name": "Death Grips",
"duration_seconds": 352,
"file_path": "/music/beware.mp3",
"tags": ["experimental", "hip-hop"]
}
],
"total": 1,
"limit": 10,
"offset": 0,
"query": "death"
}Error Response: 400 Bad Request
{
"error": "Search query required"
}The project includes comprehensive testing with pytest.
Run all tests:
pytestRun with verbose output:
pytest -vRun specific test file:
pytest test/test_api.pyRun with coverage report:
pytest --cov=srcsound-library-api/
├── src/
│ ├── __init__.py
│ ├── app.py # Flask application and routes
│ └── models.py # SQLAlchemy models
├── test/
│ ├── __init__.py
│ ├── conftest.py # pytest fixtures
│ └── test_api.py # API tests
├── requirements.txt
├── .gitignore
└── README.md
Building this project taught me:
- RESTful API Design: Proper HTTP methods, status codes, and endpoint structure
- SQLAlchemy ORM: Database relationships, especially many-to-many with association tables
- Flask Framework: Request handling, JSON responses, and application factory pattern
- Testing: Writing comprehensive integration tests with pytest and fixtures
- Input Validation: Handling edge cases and providing meaningful error messages
- Pagination: Implementing efficient pagination for large datasets
- Search Functionality: Building flexible search across multiple fields and relationships
Building this project to learn production-level backend development and prepare for backend engineering roles.