Skip to content

Repository files navigation

Jay's Learning API

A FastAPI backend serving live data about my AI/data learning journey, persisted in a real SQLite database via SQLAlchemy. The project list still syncs live from my public GitHub repos (sorted by most recently pushed) — but now that data survives a server restart, and every resource supports full CRUD.

An interactive demo frontend (index.html) is included alongside the API — see below.

Installation

pip install -r requirements.txt

Running

python main.py

API runs at http://localhost:8000. On first run, app.db (a SQLite file) is created automatically in this folder — you don't need to set anything up by hand.

Deploying (so it's live even when your computer is off)

Running this locally only makes it reachable while your own machine is on, awake, and not asleep — fine for development, not for a real "always on" demo. This API is deployed at:

https://personal-projects-api-xt21.onrender.com

render.yaml in this repo configures the deploy. If you ever need to redeploy from scratch:

  1. Push this repo to GitHub (already done if you're reading this from the repo).
  2. On Render: New +Blueprint → connect this GitHub repo. Render reads render.yaml automatically and sets up the build/start commands and a real ADMIN_API_KEY for you (auto-generated, not the local-dev fallback). If you instead connect it as a plain Web Service (not Blueprint), Render won't read render.yaml — you'd need to manually set the Start Command to uvicorn main:app --host 0.0.0.0 --port $PORT.
  3. scripts/main.js already points DEPLOYED_API_BASE at the URL above. The frontend tries your local dev server first (fast timeout), then this deployed URL (longer timeout, to tolerate Render's free-tier cold starts), then finally falls back to calling GitHub directly — so it degrades gracefully no matter what's running where.

Trade-off to know about: Render's free tier uses ephemeral disk and spins the service down after periods of inactivity. That means the SQLite database (manually added/edited projects, skills, and feedback) can reset on a cold restart — the GitHub project sync self-heals automatically on the next request, but feedback submitted between restarts won't survive indefinitely. For a learning project this is a reasonable trade-off for "always reachable, mostly persistent." If durable feedback storage matters to you, the next step up is swapping SQLite for Render's free PostgreSQL (a small change in database.py's DATABASE_URL, since SQLAlchemy abstracts most of the rest).

How data persists

database.py defines three SQLAlchemy tables — projects, skills, feedback — and creates them (and the app.db file) the moment the app starts, if they don't already exist. From then on, every request reads from and writes to that same file, so:

  • Restarting the server does not reset anything.
  • Projects/skills you add, edit, or delete via the API stay changed.
  • Feedback submitted through the form is saved for good, not just printed to the console.

You can inspect the database directly with the sqlite3 CLI (already on macOS/Linux):

sqlite3 app.db
sqlite> .tables
sqlite> SELECT * FROM projects;
sqlite> SELECT * FROM skills;
sqlite> SELECT * FROM feedback;
sqlite> .quit

Endpoints

Method Path Notes
GET / Welcome message
GET /api/projects Live-synced from GitHub + any manually-added projects
GET /api/projects/{id} A single project
POST /api/projects Add a manual project (not from GitHub)
PUT /api/projects/{id} Edit any project (GitHub-synced or manual)
DELETE /api/projects/{id} Remove a project
GET /api/skills Curated skills list
POST /api/skills Add a skill
PUT /api/skills/{id} Edit a skill
DELETE /api/skills/{id} Remove a skill
POST /api/feedback Submit feedback (public, validated)
GET /api/feedback List all feedback — requires X-API-Key header
DELETE /api/feedback/{id} Delete one feedback entry — requires X-API-Key header

API Documentation

Visit http://localhost:8000/docs for interactive, auto-generated API documentation (Swagger UI) — try every endpoint directly from the browser (the two admin routes need you to paste the X-API-Key header in via the "Authorize" — actually just use curl or the header field on each request, Swagger's built-in auth UI isn't wired up for this).

Feedback privacy — the admin key

Feedback submissions include real names and emails, so listing or deleting them requires an X-API-Key header:

curl http://localhost:8000/api/feedback -H "X-API-Key: your-key-here"

By default (no env var set) it uses a fallback key hardcoded in security.py (local-dev-only-change-me) — fine for local development, and the server prints a loud warning on startup to remind you it's active. Before deploying this anywhere public, set a real secret:

export ADMIN_API_KEY="something-long-and-random"
python main.py

POST /api/feedback (submitting feedback) stays public with no key needed — that's the whole point of a feedback form.

How the live project sync works

routes/projects.py calls GitHub's public REST API (api.github.com/users/sankara226/repos) sorted by pushed date, and upserts the top 6 into the projects table (matched by name). This sync is rate-limited to once per 5 minutes in-memory, well under GitHub's unauthenticated limit (60 requests/hour/IP). If GitHub is unreachable and there's no prior synced data yet, it bootstraps from the static snapshot in data/projects.json just once, so the demo never starts up empty. The response includes a "source" field ("github", "cached", "stale-cache", or "fallback") so you can always tell what happened.

Note: the Project model has updated_at and stars (pulled straight from GitHub) instead of a week number — a made-up week count wouldn't mean anything for arbitrary real repos.

Frontend demo

index.html (+ styles/, scripts/) is a small animated dashboard that visualizes this API: live project cards, a skills section, a "try it live" API explorer, and a feedback form. It tries the local backend first (http://localhost:8000) and, if that's not running, calls GitHub's API directly from the browser instead — so the live "6 most recent repos" view works for any visitor, whether or not you have main.py running. Just open index.html, no server required.

Connect to a different frontend

fetch('http://localhost:8000/api/projects')
  .then(r => r.json())
  .then(data => console.log(data))

Quality checklist

  • ✅ Database file (app.db) created automatically on first run
  • ✅ Data persists across server restarts
  • ✅ Full CRUD on projects and skills; create/read/delete on feedback
  • ✅ Proper data models (Pydantic validation, including real email format checking)
  • ✅ Organized routing (separate files under routes/)
  • ✅ CORS enabled (callable from any frontend)
  • ✅ Auto-documentation (/docs)
  • ✅ Sensitive data (feedback) gated behind an API key, not left publicly scrapeable
  • ✅ README with clear setup, database, and usage instructions

About

A FastAPI backend and dashboard serving real-time portfolio metrics, featuring live GitHub API integration, in-memory caching, graceful fallback mechanisms, and Pydantic validation.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages