temi.dev — AI-Powered Portfolio Platform
temi.dev—AI-PoweredPortfolioPlatform
The platform behind this site — a full-stack monorepo with an AI search layer, built so the portfolio is itself the case study.
The Problem
A portfolio is usually a static site. I wanted mine to be the thing it describes: a real full-stack product with a backend, an AI search layer, and an admin surface — so visiting the site is reading the case study.
Architecture Thinking
The guiding decision was to over-build deliberately — treat a personal site like a product, so the architecture choices are real ones. It's a pnpm + Turborepo monorepo: a Next.js 15 (App Router, React 19) frontend, a NestJS-on-Fastify backend, and shared ui, types, and config packages, with all content database-driven through a JWT-protected admin rather than hardcoded. The one genuinely interesting subsystem is search.
RAG Without a Separate Vector Database
Posts and projects are embedded with Gemini and stored as vectors in the same PostgreSQL via pgvector, so semantic search is just a SQL query sitting next to the relational data — no extra service, nothing to keep in sync:
SELECT id, title, content,
1 - (embedding <=> $queryVec) AS similarity
FROM "Project"
WHERE embedding IS NOT NULL
AND 1 - (embedding <=> $queryVec) >= 0.7 -- relevance floor
ORDER BY embedding <=> $queryVec ASC -- cosine distance
LIMIT 8;
A visitor's question is embedded, matched against this, and answered from the retrieved context — with "I don't know" treated as a valid response rather than a hallucination.
Production Hygiene
The API ships with Helmet security headers, tiered rate limiting, request validation, auto-generated Swagger docs, and Sentry error tracking. CI gates every push on lint and type-checking across the whole monorepo, and Open Graph cards are generated as branded images at the edge.
Constraints
It's a portfolio, so it has to load fast and look polished — but I used it as a place to practice production patterns instead of cutting corners, which is why it carries real auth, validation, observability, and CI. Keeping the AI layer cheap and simple — no separate vector database, a relevance floor to keep junk out of the context — was a deliberate constraint, not a shortcut.
What It Demonstrates
- Embeddings in Postgres —
pgvectorcosine search lives beside relational data, so there's no separate vector service to run or sync. - Grounded retrieval — a 0.7 similarity floor keeps weak matches out of the context, so the assistant can honestly say "I don't know."
- Monorepo architecture — Turborepo pipelines and shared packages keep a Next.js app and a NestJS API consistent.
- Production hygiene by default — security headers, rate limiting, Swagger, Sentry, and a blocking CI gate, on a personal project.
Outcome
The site you're reading is the deliverable: a monorepo with a real API, embeddings-backed search, an admin CRM for projects, posts, and leads, and a CI/CD pipeline. It's deliberately over-built for a portfolio — that's the point.