This repository contains a RESTful API for managing a product catalog. Built with FastAPI, PostgreSQL, and SQLAlchemy, it provides a robust foundation for creating, reading, updating, and deleting users and products. The entire application is containerized using Docker and Docker Compose for a simplified setup and deployment process.
- Modern Python Backend: Built with Python 3.13 and the high-performance FastAPI framework.
- Asynchronous Support: Leverages
asyncpgand SQLAlchemy's async capabilities for non-blocking database operations. - JWT Authentication: Secure authentication system with role-based access control.
- Role-Based Authorization: Admin and anonymous user roles with different permission levels.
- Containerized Environment: Fully containerized with Docker and orchestrated with Docker Compose for consistency across development and production environments.
- Database Migrations: Uses
dbmatefor clear, simple, and plain SQL database migrations. - Data Validation: Employs Pydantic for robust data validation and serialization.
- Structured Project: A clean and organized project structure that separates concerns like database logic, API endpoints, and business logic.
- Custom Exception Handling: Provides clear, user-friendly error messages for request validation failures.
- Framework: FastAPI
- Database: PostgreSQL
- ORM: SQLAlchemy 2.0 (async)
- Containerization: Docker, Docker Compose
- Data Validation: Pydantic
- Migrations: Dbmate
- Web Server: Uvicorn
Follow these instructions to get the project running on your local machine.
-
Clone the repository:
git clone https://github.com/carlosochoa8/catalog_api.git cd catalog_api -
Create an environment file: Create a
.envfile in the root directory of the project by copying the example below. These variables are used by Docker Compose to configure the services.# Application Image Name IMAGE_NAME=catalog_api # PostgreSQL Configuration POSTGRES_PORT=5432 POSTGRES_HOST=db POSTGRES_USER=postgres POSTGRES_PASSWORD=postgres POSTGRES_NAME=postgres # Database URLs DATABASE_URL=postgresql+asyncpg://${POSTGRES_USER}:${POSTGRES_PASSWORD}@${POSTGRES_HOST}:${POSTGRES_PORT}/${POSTGRES_NAME} DATABASE_URL_DBMATE=postgresql://${POSTGRES_USER}:${POSTGRES_PASSWORD}@${POSTGRES_HOST}:${POSTGRES_PORT}/${POSTGRES_NAME}?sslmode=disable # JWT Configuration AUTH_SECRET_KEY=your-super-secret-key-here AUTH_ALGORITHM=HS256 AUTH_ACCESS_TOKEN_EXPIRE_MINUTES=30
-
Build and run the services using Docker Compose:
docker-compose up --build
This command will:
- Build the Docker image for the FastAPI application (
catalog-service). - Start the PostgreSQL database container (
db). - Run database migrations automatically using the
migrationservice. - Start the FastAPI application.
- Build the Docker image for the FastAPI application (
-
Access the API: The API will be available at
http://localhost:8080. The interactive API documentation (Swagger UI) can be accessed athttp://localhost:8080/docs.
The API is served under the /catalog_api root path.
- GET
/catalog_api/
Returns a simple JSON response to indicate that the service is running.
Success Response (200 OK):
{
"Message": "Ok"
}- POST
/catalog_api/authenticate/
Authenticates a user and returns a JWT access token.
Request Body:
{
"email": "admin@example.com",
"password": "YourPassword123!"
}Success Response (200 OK):
{
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"token_type": "bearer"
}Note
User creation requires admin authentication. Include the JWT token in the Authorization header: Authorization: Bearer <token>
- POST
/catalog_api/users/π Admin Only
Creates a new user in the system. Only admin users can create other users.
Headers:
Authorization: Bearer <jwt_token>
Request Body:
{
"email": "newuser@example.com",
"password": "SecurePass123!",
"user_type": "anonymous"
}Success Response (201 Created):
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"email": "newuser@example.com",
"user_type": "anonymous",
"created_at": "2025-08-13 20:15:30",
"updated_at": "2025-08-13 20:15:30"
}- POST
/catalog_api/products/π Admin Only
Creates a new product in the system.
Headers:
Authorization: Bearer <jwt_token>
Request Body:
{
"sku": "PROD-001",
"name": "Sample Product",
"price": 99.99,
"brand": "Nike"
}Success Response (201 Created):
{
"id": "550e8400-e29b-41d4-a716-446655440001",
"sku": "PROD-001",
"name": "Sample Product",
"price": 99.99,
"brand": "Nike",
"created_at": "2025-08-13 20:15:30",
"updated_at": "2025-08-13 20:15:30"
}- GET
/catalog_api/products/{product_id}π Public
Retrieves product information by ID. Available to all users (no authentication required).
Success Response (200 OK):
{
"id": "550e8400-e29b-41d4-a716-446655440001",
"sku": "PROD-001",
"name": "Sample Product",
"price": 99.99,
"brand": "Nike",
"created_at": "2025-08-13 20:15:30",
"updated_at": "2025-08-13 20:15:30"
}- PUT
/catalog_api/products/{product_id}π Admin Only
Updates an existing product.
Headers:
Authorization: Bearer <jwt_token>
Request Body:
{
"name": "Updated Product Name",
"price": 149.99
}Success Response (200 OK):
{
"id": "550e8400-e29b-41d4-a716-446655440001",
"sku": "PROD-001",
"name": "Updated Product Name",
"price": 149.99,
"brand": "Nike",
"created_at": "2025-08-13 20:15:30",
"updated_at": "2025-08-13 20:25:45"
}- DELETE
/catalog_api/products/{product_id}π Admin Only
Deletes a product from the system.
Headers:
Authorization: Bearer <jwt_token>
Success Response (204 No Content)
The API uses JWT (JSON Web Tokens) for authentication and role-based access control:
- Anonymous Users: Can only retrieve product information (GET endpoints)
- Admin Users: Can create, update, and delete users and products
Passwords must meet the following criteria:
- 8-25 characters long
- At least one uppercase letter
- At least one lowercase letter
- At least one number
- At least one special character (!@#$%&*._)
- No spaces allowed
anonymous: Standard users with read-only access to productsadmin: Administrative users with full CRUD access to users and products
The API returns consistent error responses in the following format:
Authentication Error (400):
{
"detail": "Could not validate credentials"
}Validation Error (422):
{
"detail": [
{
"loc": ["body", "email"],
"msg": "field required",
"type": "value_error.missing"
}
]
}Not Found Error (404):
{
"detail": "Product not found"
}