A production-ready REST API for employee management and salary calculations. No UI; API only.
- Employee CRUD — Create, read, update, delete employees (full name, job title, country, salary).
- Salary calculation — Compute deductions (TDS by country) and net salary from an employee’s gross salary.
- Salary metrics — Min/max/average salary by country; average salary by job title.
Stack: Python 3.11+, FastAPI, SQLAlchemy 2.0 (async + aiosqlite), SQLite3, pytest. All I/O is async; errors are handled with appropriate HTTP status codes and validation.
python -m venv .venv
source .venv/bin/activate # Windows: .venv\Scripts\activate
pip install -r requirements.txt
# Set env if needed (default: SQLite file in project)
uvicorn app.main:app --reload- API: http://localhost:8000
- Interactive API docs: http://localhost:8000/docs
- OpenAPI JSON: http://localhost:8000/openapi.json
docker-compose up --buildSame URLs as above (port 8000).
pytest
# Or in Docker:
docker-compose run app pytestpip install -r requirements.txt && pytest -q && uvicorn app.main:app --host 127.0.0.1 --port 8000Then open http://localhost:8000/docs or run: curl http://localhost:8000/health → {"status":"ok"}.
| Variable | Description | Default |
|---|---|---|
DATABASE_URL |
SQLite URL: sqlite+aiosqlite:///./payinsight.db (or absolute path) |
sqlite+aiosqlite:///./payinsight.db |
- Local:
http://localhost:8000 - All request/response bodies are JSON unless noted.
- GET /health
- Response:
200—{ "status": "ok" }(or similar)
- Response:
-
POST /api/employees
- Body:
{ "full_name": string, "job_title": string, "country": string, "salary": number } - Response:
201— created employee (withid, timestamps if applicable).
- Body:
-
GET /api/employees
- Response:
200— list of employees.
- Response:
-
GET /api/employees/{id}
- Response:
200— single employee;404if not found.
- Response:
-
PUT /api/employees/{id}
- Body: same as create (all fields optional for partial update).
- Response:
200— updated employee;404if not found.
-
DELETE /api/employees/{id}
- Response:
204no content;404if not found.
- Response:
Validation errors: 422 with detail array.
- GET /api/salary/calculation/{employee_id}
- Uses the employee’s stored salary as gross.
- Deduction rules:
- India: TDS 10% of gross → net = gross − TDS
- United States: TDS 12% of gross → net = gross − TDS
- All other countries: No deductions → net = gross
- Response:
200— e.g.{ "gross": number, "deduction": number, "net": number } 404if employee not found.
-
GET /api/salary/metrics/by-country?country=India
- Response:
200—{ "min": number, "max": number, "average": number } 404if no employees in that country.
- Response:
-
GET /api/salary/metrics/by-job-title?job_title=Engineer
- Response:
200—{ "average": number } 404if no employees with that job title.
- Response:
- Dockerfile: Multi-stage (optional): build deps → runtime with
uvicorn app.main:app. - docker-compose.yml: Service
appwith port 8000, env forDATABASE_URL, volume for SQLite file if persistent DB desired.
Run:
docker-compose up --buildTests:
docker-compose run app pytestSee STRUCTURE.md for the full tree. Summary:
app/
├── main.py # FastAPI app, lifespan, routes
├── config.py # Settings (DATABASE_URL)
├── database.py # SQLAlchemy async engine, session, CRUD
├── exceptions.py # Global 500 handler
├── models/ # SQLAlchemy ORM (Employee)
├── schemas/ # Pydantic (EmployeeCreate, EmployeeResponse, ...)
├── routes/ # employees, salary
└── services/ # salary (deduction rules)
tests/
├── conftest.py # client, clean_db fixtures
├── test_health.py
├── test_db_employees.py
├── test_api_employees.py
├── test_salary_calculation.py
└── test_salary_metrics.py
Be intentional and transparent: note where and how you used AI (tools, prompts, rationale).
- Scaffolding: Project structure, AGENTS.md, TDD_TODO.md, STRUCTURE.md, and README skeleton were generated with Cursor from the Backend Problem Statement (TDD, production-ready, async, SQLite).
- Code generation: FastAPI routes, Pydantic schemas, SQLAlchemy async layer (models, database.py, CRUD), and salary deduction service were drafted with AI; refined for async session handling and test isolation.
- Tests: pytest test cases (DB layer, API CRUD, salary calculation, salary metrics) were generated from AGENTS.md; test DB isolation (file-based test DB, unique country/job_title in metrics tests) and
init_dbinget_sessionwere added so all tests pass reliably. - Documentation: README (quick start, env vars, API paths, folder structure) and this Implementation Details section were drafted with AI and updated to match the final API and SQLAlchemy stack.
- Trade-offs: Used SQLAlchemy 2.0 async + aiosqlite (as requested) for full async; file-based test DB and idempotent
init_db()inget_sessionfor test stability; global exception handler for 500 safety.
[Your choice — e.g. MIT, or as required by the hiring exercise.]