Skip to content

Repository files navigation

personal-api

CIPythonLicense: MIT

Your own data API — now-playing, projects, and blog posts as clean JSON — plus a thin portfolio frontend that consumes it. Content lives as markdown + JSON on disk, so publishing a post is just adding a file.

screenshot

Run it

pip install -r requirements.txt
uvicorn app.main:app --reload # http://localhost:8000 (portfolio + /docs)

API

RouteReturns
GET /api/profilename, headline, links
GET /api/nownow-playing / current activity (wire to Spotify/Last.fm in prod)
GET /api/projectsproject list
GET /api/postspost metadata (title, date, summary, tags); ?tag= to filter
GET /api/posts/{slug}full post incl. body
GET /api/tagsevery tag with its post count, most-used first
GET /feed.xmlan RSS 2.0 feed of all posts

The single-post route reads exactly one file and rejects unsafe slugs (no path traversal). /feed.xml XML-escapes every field, so titles with & / < can't break the feed.

Add a blog post

Drop a markdown file in content/posts/ with front matter:

---title: My new postdate: 2026-06-01summary: One-line teaser.tags: [typescript, react]---
Post body here.

Posts are returned newest-first automatically.

Design

The content layer (app/content.py) is a set of pure functions over a directory — front-matter parsing, post loading, profile loading — so it's tested against a fixture folder without spinning up the server. The frontend is intentionally "dumb": it just fetches the API and renders.

Development

python -m pytest -q # 19 tests (content parsing, tag/RSS helpers, traversal safety + API endpoints)

License

MIT

About

Your own data API (now-playing, posts, projects) plus a portfolio frontend that consumes it.

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages