This is the personal portfolio of Jesse Peña, showcasing projects, professional experience, and a custom Final Fantasy Quotes experience powered by a database and API.
- Home & Projects — static pages built with HTML/CSS/JS.
- Final Fantasy Quotes Page — lets visitors roll a random quote or browse by game, character, tag, and text search.
- FF Quotes API — a Node/Express service that serves quotes from a serverless SQLite database (Turso / libSQL).
- Frontend (static site):
- Hosted on GitHub Pages at
https://jpchato.github.io/personal_portfolio/. - The quotes page (
pages/ff-quotes.html) calls the API from the browser.
- Hosted on GitHub Pages at
- Backend (API):
- Node.js + Express, deployed as a Render Web Service.
- Live base URL:
https://personal-portfolio-1jm1.onrender.com(see/api/*routes below).
- Database (persistence):
- Turso (serverless SQLite via libSQL). The API connects with
@libsql/client.
- Turso (serverless SQLite via libSQL). The API connects with
Table: quotes
id INTEGER PRIMARY KEY AUTOINCREMENT
text TEXT NOT NULL
game TEXT
character TEXT
source TEXT
tags TEXT NOT NULL DEFAULT '[]' -- JSON array, stored as string
created_at TEXT NOT NULL -- ISO timestamp
- Filtering supports
game,character,tag, and full‑text q (LIKE ontext,character,game). - Tag filter uses SQLite/SQL JSON functions (via libSQL) to match values inside the
tagsJSON array. - Random uses
ORDER BY RANDOM() LIMIT 1with any filters you pass. - Pagination via
limit(1–100) andoffset.
- Writes protected:
POST /api/quotesrequiresAuthorization: Bearer <API_TOKEN>. - Rate limiting: production limits write attempts (default 20 requests / 10 minutes per token/IP). Exceeding returns HTTP
429. - CORS: explicitly allows your dev and production origins only.
- Proxy trust: Express trusts the hosting proxy so client IPs are accurate.
- Frontend: HTML, CSS, vanilla JS (with a light FF‑menu vibe).
- Backend: Node.js + Express.
- Database: Turso (libSQL / serverless SQLite) via
@libsql/client. - Hosting: GitHub Pages (frontend) + Render (API).
- Node 18+ and npm
- Clone and enter the repo:
git clone https://github.com/jpchato/personal_portfolio.git
cd personal_portfolio- Create
.envin the repo root:
PORT=5173
API_TOKEN=replace-with-a-strong-secret
TURSO_DB_URL=libsql://<your-db>.turso.io
TURSO_DB_TOKEN=<your-turso-token>- Install and run the API locally:
npm install
npm run dev
# API: http://localhost:5173- Serve the static site locally (any static server works). For example:
# Python
python -m http.server 5500
# Site: http://localhost:5500The quotes page autodetects dev vs prod and points to the right API base.
If you have seed files, use the provided script:
# seeds/quotes.json and/or seeds/extra-quotes.json
npm run seed:libsql -- seeds/quotes.json
npm run seed:libsql -- seeds/extra-quotes.jsonCommon sanity checks:
curl http://localhost:5173/api/health
curl http://localhost:5173/api/quotes?limit=5
curl http://localhost:5173/api/quotes/randomBase URL (prod): https://personal-portfolio-1jm1.onrender.com/api
GET /health— health checkGET /quotes— list with filters & pagination- query params:
game,character,tag,q,limit,offset
- query params:
GET /quotes/random— one random quote (supports same filters)POST /quotes— add quote (requiresAuthorization: Bearer <API_TOKEN>)- body:
{ text, game?, character?, source?, tags?[] }
- body:
# list
curl "https://personal-portfolio-1jm1.onrender.com/api/quotes?limit=3"
# random with filter
curl "https://personal-portfolio-1jm1.onrender.com/api/quotes/random?game=FFXVI"
# add quote (authorized)
TOKEN=your-strong-secret
curl -X POST "https://personal-portfolio-1jm1.onrender.com/api/quotes" \
-H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
-d '{
"text": "A smile better suits a hero.",
"game": "FFXIV",
"character": "Haurchefant Greystone",
"source": "The Vault",
"tags": ["ff14","heavensward"]
}'- Deploys from the repository
gh-pagesor main branch per your Pages settings. - The quotes page points to the live API when not on
localhost.
- Build:
npm install - Start:
node server.js - Health check path:
/api/health - Environment variables:
NODE_ENV=productionAPI_TOKEN=<your secret>TURSO_DB_URL=libsql://<your-db>.turso.ioTURSO_DB_TOKEN=<your token>
If you add dependencies or change build behavior, push to Git. Render automatically redeploys on new commits (or click Manual Deploy → Clear build cache & deploy for a fresh install).
This portfolio and the FF Quotes system were built with ChatGPT (GPT‑5 Thinking) as a key facilitator for:
- Architectural design (frontend ↔ API ↔ Turso), environment setup, and CORS strategy.
- Implementing the Node/Express API, database schema, and query logic (filters, random, pagination).
- Integrating Turso / libSQL and preparing seeding scripts.
- Adding rate limiting and production‑safe defaults.
- Deploying to Render and wiring the static site to the live API.
MIT License.