It's a full-stack demonstration application built with Effect, featuring an AI-powered Resume Parser and score system. This project showcases how to use structured LLM extraction to derive deterministic assessments. The filosophy is use LLMs for what they do better: turn messy unstructured information into typed data that we can then model deterministically and integrate into larger AI/ML pipelines. It's a key pattern for regulated industries, where systems should run as deterministically and explanable as possible while handling documents with a wide range of formats and structures.
This repository demonstrates how to build production-grade applications with the Effect ecosystem. It combines two major domains:
- File Management: Resumable uploads via UploadThing, folder organization, and multi-user isolation.
- AI Resume Analysis: PDF parsing, structured entity extraction using BAML (Basically A Made-Up Language), and context-aware scoring based on job roles and company profiles.
- Structured Extraction: Uses BAML to guarantee type-safe extraction of experience, education, skills, and contact info from PDFs.
- Streaming Responses: Real-time UI updates as the LLM parses the document (partial results via WebSocket).
- Context Scoring: Dynamic candidate scoring algorithm based on target Position (e.g., Frontend, Tech Lead) and Company Profile (e.g., Startup, Enterprise).
- Dealbreaker Detection: Logic to identify missing certifications or experience gaps based on context.
- UploadThing Integration: Secure, resumable file uploads.
- Real-time Sync: WebSocket events update file lists across clients instantly.
- File Operations: Create folders, move files, and delete items with optimistic UI updates.
- Contract-First RPC: Type-safe communication between Client and Server using
@effect/rpc. - Reactive Client: State management using
@effect-atom/atom-react. - Observability: Full OpenTelemetry tracing with Jaeger.
Prerequisites:
- Node.js 22.x
- pnpm 10.x
- Docker (for PostgreSQL & Jaeger)
- An OpenAI API Key (or Anthropic)
pnpm installCopy the example environment file:
cp .env.example .envYou must configure the following in .env:
DATABASE_URL: (Default is set for Docker)UPLOADTHING_TOKEN: Get this from your UploadThing dashboard (v7).OPENAI_API_KEY: Required for the Resume Parser (or configureanthropicinbaml_src/clients.baml).
Start the infrastructure and prepare the code generation:
# Start Postgres and Jaeger
docker compose up -d
# Run database migrations
pnpm db:migrate
# Generate BAML client code (required for AI features)
pnpm baml:generateRun the client and server in separate terminals:
# Terminal 1: API Server
pnpm dev:server # http://localhost:3001
# Terminal 2: React Client
pnpm dev:client # http://localhost:5173- App: http://localhost:5173
- Jaeger UI: http://localhost:16686 (Trace ID available in server logs)
packages/
domain/ # Shared Schemas, RPC Contracts (Files + Resume), and Policy
server/ # Node.js runtime, BAML definitions, DB Logic
client/ # React, TanStack Router, Effect Atoms
- Upload: User uploads a PDF via the Client.
- RPC Call: Client calls
resume_parse(fileId). - Server:
- Retrieves file URL from UploadThing.
- Invokes BAML (
packages/server/baml_src/resume.baml) to prompt the LLM. - Streams partial results back to the client via
Stream.async.
- Scoring: Once parsing is complete,
packages/server/src/public/resume/scoring-logic.tscalculates a score (0-1000) based on weighted dimensions (Skills, Experience, etc.). - Persistence: Structured data is stored in PostgreSQL.
- Runtime: Effect (TypeScript)
- AI/LLM: BAML (Boundary)
- RPC: @effect/rpc (WebSocket/NDJSON)
- Frontend: React 19, Tailwind v4, TanStack Router
- State: @effect-atom/atom-react
- Database: PostgreSQL, @effect/sql
- Storage: UploadThing
| Command | Description |
|---|---|
pnpm baml:generate |
Generate TypeScript clients from .baml files |
pnpm db:migrate |
Apply SQL migrations |
pnpm db:reset |
Wipe database and re-apply schema |
pnpm check |
Run TypeScript type checking |
pnpm lint |
Run Oxlint and ESLint |
pnpm test |
Run Vitest suite |
Huge thanks to Lucas Barake for his amazing video tutorials on how to build type-safe, resilient applications and the Uploadthing integration demo (which is the foundation of this project). Also huge thanks to the Effect community as a whole - it's being a long way to learn new patterns of error handling and concurrency, but I have never seen such a talented and helpful community (Effect Office Hours by Kit and Max have enlightned me many times haha).
Last but not least, 🦄 ai that works by dex and hellovai as always.
MIT