
CareerGlyph
A developer profile platform where skills, projects, and peer endorsements replace static resumes. REST API with JWT auth, compound-key endorsements, and full Swagger documentation.
Performance & Impact
71 tests
Test Suite
58 unit + 13 E2E tests across 5 spec files
9 endpoints
API Surface
Auth (2) + Profile CRUD (4) + Skills/Projects/Endorsements (3)
JWT + bcrypt
Auth Security
Rate-limited registration, bcrypt password hashing, Passport JWT strategy
The Problem
A PDF resume cannot show code quality, real contributions, or whether skills are endorsed by people who've actually worked with you. Skills on LinkedIn are self-reported with no verification.
The Solution
A live API-backed profile: skills are added by the developer, projects link to live URLs and GitHub, and endorsements come from other developers who authenticate first. The compound key on endorsements means each developer can endorse each skill exactly once.
System Architecture
CareerGlyph replaces static resume PDFs with live, verifiable developer profiles. The NestJS backend provides JWT authentication (register + login with bcrypt, rate-limited via @nestjs/throttler), a full skills CRUD API with SkillCategory/SkillLevel enums, project management, and a peer endorsement system. Endorsements use a compound unique index (skillId + giverId) with Prisma upsert — so endorsing twice updates the message rather than creating duplicates. All 9 endpoints are protected by JwtAuthGuard and documented in Swagger.
Core Engineering Achievements:
System Architecture
API Layer
Business Logic
Data Layer
"Standard NestJS layered architecture: controllers delegate to services, services talk to Prisma, all DB queries are type-safe. The interesting design decision is the endorsement compound key — it makes the upsert pattern trivial and prevents duplicate endorsements at the DB level."
The Engineering Challenge
The endorsement upsert pattern was the key design decision. A naive implementation would check-then-insert (two queries, race condition). Using Prisma's upsert with the compound key makes it atomic — create if not exists, update if exists. The other non-obvious decision was ordering NestJS routes: static routes (me, me/skills) must be declared before the :username param route, otherwise NestJS matches 'me' as a username.
User Journey
Interested in the full engineering breakdown?
I'm always open to discussing technical implementations, from state management strategies to infrastructure scaling.