Skip to content

Repository files navigation

rest-api-starter

A JWT-authenticated REST API starter: FastAPI, SQLAlchemy, per-user resource ownership enforced at the query level, and a Postgres-ready deployment that runs on SQLite with zero setup for local development.

Problem

"Build me a REST API with user accounts" is one of the highest-volume request types on any freelance platform, and most of what makes it tedious isn't the business logic — it's auth (hashing, tokens, the /register//login dance), making sure user A can't read or edit user B's data, and a database setup that works locally without a Postgres server running.

Solution

Auth, ownership, and database wiring are solved once here so a specific project's actual resources (Task in this starter — swap the name, the CRUD/auth/ownership pattern doesn't change) don't have to re-solve them:

  • JWT authbcrypt for hashing, python-jose for tokens, wired through FastAPI's OAuth2PasswordBearer so /docs gets a working "Authorize" button for free.
  • Ownership enforced in the query, not after it — every task lookup filters by owner_id == current_user.id in the database query itself; a task that exists but belongs to someone else returns 404, not 403 (403 would confirm the ID exists to a caller who doesn't own it).
  • SQLite by default, Postgres via one environment variableDATABASE_URL unset runs against a local file with no setup; set it to a Postgres URL in docker-compose.yml or production with no code change.

Architecture

flowchart LR
    Client(["client"]) -->|"POST /auth/register\nPOST /auth/login"| AuthRouter["auth router"]
    AuthRouter -->|"bcrypt hash / verify"| Auth["auth.py"]
    AuthRouter -->|"JWT"| Auth
    Client -->|"Bearer token"| Dep["get_current_user\n(dependency)"]
    Dep -->|"decode JWT"| Auth
    Dep --> TasksRouter["tasks router"]
    Client -->|"GET/POST/PATCH/DELETE /tasks"| TasksRouter
    TasksRouter -->|"filter by owner_id"| DB[("SQLite (dev)\nPostgres (prod)")]
    AuthRouter --> DB
Loading

Installation

git clone https://github.com/kestrelquant/rest-api-starter
cd rest-api-starter
pip install -r requirements.txt
uvicorn app.main:app --reload

Interactive docs (with a working "Authorize" button once you have a token) at http://localhost:8000/docs.

With Docker (Postgres included)

cp .env.example .env   # set JWT_SECRET_KEY
docker compose up -d

Usage

curl -X POST http://localhost:8000/auth/register \
  -H "Content-Type: application/json" \
  -d '{"email": "smoke@test.com", "password": "hunter22"}'
# {"id":1,"email":"smoke@test.com"}

curl -X POST http://localhost:8000/auth/login \
  -d "username=smoke@test.com&password=hunter22"
# {"access_token":"eyJ...","token_type":"bearer"}

curl -X POST http://localhost:8000/tasks \
  -H "Authorization: Bearer eyJ..." \
  -H "Content-Type: application/json" \
  -d '{"title": "Ship it"}'

The register/health responses above are copied from an actual local run of this exact code, not hand-written.

Tests

19 tests, fully isolated — each test gets its own in-memory SQLite database via a fixture, so nothing leaks between tests or touches a real app.db file.

pip install -r requirements-dev.txt
python -m pytest tests/

CI (.github/workflows/ci.yml) runs the suite on Python 3.10–3.12 and builds the Docker image.

Contributing

See CONTRIBUTING.md.

License

MIT — see LICENSE.

About

JWT-authenticated FastAPI REST starter with per-user resource ownership enforced at the query level

Topics

Resources

Contributing

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages