A FastAPI backend for user accounts and text logs this was specifically made for a roblox game i was working on where I wanted our developers to post logs from a studio plugin!
NOTE: currently account creation is being restrained by a header token being needed, i dont want any joe shmoe from adding a user to the db
- FastAPI for the HTTP API surface
- SQLAlchemy ORM with SQLite storage
- Pydantic for request validation at the API boundary
- msgspec Structs for service/core contracts
- JWT bearer tokens for authentication
- FastAPI: https://fastapi.tiangolo.com/
- FastAPI Dependencies: https://fastapi.tiangolo.com/tutorial/dependencies/
- FastAPI Security (OAuth2/JWT patterns): https://fastapi.tiangolo.com/tutorial/security/oauth2-jwt/
- SQLAlchemy ORM 2.0: https://docs.sqlalchemy.org/en/20/orm/
- SQLAlchemy Relationships: https://docs.sqlalchemy.org/en/20/orm/basic_relationships.html
- SQLAlchemy Typed Mappings (Mapped/mapped_column): https://docs.sqlalchemy.org/en/20/orm/declarative_tables.html
- Pydantic Fields: https://docs.pydantic.dev/latest/concepts/fields/
- msgspec Structs: https://jcristharif.com/msgspec/structs.html
- JWT Standard (RFC 7519): https://datatracker.ietf.org/doc/html/rfc7519
- src/main.py: app entrypoint for Uvicorn
- src/app.py: FastAPI app factory and router registration
- src/db/session.py: engine/session/Base and DB dependency
- src/models/: SQLAlchemy models
- src/schemas/: API request schemas (Pydantic)
- src/schemas/core/: service/core contracts (msgspec)
- src/repo/: data access layer
- src/services/: business logic layer
- src/security/: password hashing and JWT helpers
- src/api/routers/: HTTP routes
- src/util/response.py: unified response pipeline
All endpoints return a consistent envelope:
- code: status code label, for example SUCCESS, NOT_FOUND, ALREADY_EXISTS
- detail: human-readable summary
- result: payload object/list/null
Example success:
{
"code": "SUCCESS",
"detail": "User retrieved successfully",
"result": {
"id": 1,
"username": "camey",
"created_at": "2026-04-13T20:40:11.123456"
}
}Example error:
{
"code": "ALREADY_EXISTS",
"detail": "Username camey already exists",
"result": null
}- Register a user with POST /api/users/ and header X-User-Creation-Token
- Login with POST /api/users/login to receive access_token
- Send token in Authorization header:
Authorization: Bearer <access_token>- Access protected routes such as /api/logs/ and /api/users/{id}
- POST /api/users/ (requires X-User-Creation-Token): create account
- POST /api/users/login (public): login and receive bearer token
- GET /api/users/{user_id} (auth): get own user
- PATCH /api/users/{user_id} (auth): modify own user
- DELETE /api/users/{user_id} (auth): delete own user
- GET /api/logs/ (auth): list logs (supports optional user_id, offset, limit query params)
- GET /api/logs/{log_id} (auth): get one owned log
- POST /api/logs/ (auth): create log (optional created_at override)
- PATCH /api/logs/{log_id} (auth): modify owned log
- WS /api/logs/ws?token= (auth): subscribe to log.created events
pip install -r requirements.txt- JWT_SECRET
- USER_CREATION_TOKEN
- DATABASE_URL (default: sqlite:///./sff.db)
- JWT_EXPIRE_MINUTES (default: 60)
Codespaces note:
- Store JWT_SECRET and USER_CREATION_TOKEN as Codespaces secrets.
- They are exposed as environment variables at runtime, so no dotenv file is required.
PowerShell example:
$env:JWT_SECRET = "super-secret-value"
$env:USER_CREATION_TOKEN = "long-random-create-token"
$env:JWT_EXPIRE_MINUTES = "120"uvicorn src.main:app --reload- Swagger UI: http://127.0.0.1:8000/docs
- ReDoc: http://127.0.0.1:8000/redoc
Create user:
curl -X POST http://127.0.0.1:8000/api/users/ \
-H "X-User-Creation-Token: YOUR_CREATE_TOKEN" \
-H "Content-Type: application/json" \
-d '{"username":"camey","password":"securePass123"}'Login:
curl -X POST http://127.0.0.1:8000/api/users/login \
-H "Content-Type: application/json" \
-d '{"username":"camey","password":"securePass123"}'Create log with server timestamp (replace TOKEN):
curl -X POST http://127.0.0.1:8000/api/logs/ \
-H "Authorization: Bearer TOKEN" \
-H "Content-Type: application/json" \
-d '{"message":"first log entry"}'Create log with timezone-aware timestamp override:
curl -X POST http://127.0.0.1:8000/api/logs/ \
-H "Authorization: Bearer TOKEN" \
-H "Content-Type: application/json" \
-d '{"message":"logged from mobile","created_at":"2026-04-13T21:42:00-05:00"}'Notes for created_at override:
- Field is optional.
- Must include a timezone offset (for example
Zor-05:00). - Server preserves the submitted wall-clock timestamp when an override is provided.
Subscribe to realtime log events via WebSocket:
ws://127.0.0.1:8000/api/logs/ws?token=<access_token>
- Tables are auto-created at startup through Base.metadata.create_all(...)
- Passwords are stored as PBKDF2-SHA256 hashes, never plaintext.