Product research platform that scrapes community posts from Reddit and HackerNews, analyzes them with Claude AI to extract actionable insights, and generates product specs.
- Community Scraping — Automated collection from Reddit (JSON API) and HackerNews (Firebase + Algolia)
- AI-Powered Analysis — Claude extracts pain points, competitors, trends, and sentiment from raw posts
- Spec Generation — Generates product specs in Markdown, Claude Code, or Linear formats
- Discovery Engine — Category-based onboarding with automated insight feeds
- Auth & Multi-tenancy — JWT authentication with full data isolation per user
- Background Processing — BullMQ job queue for scraping, analysis, and generation pipelines
- Node.js >= 20
- Docker & Docker Compose
- Anthropic API key (for AI analysis and spec generation)
# Set required environment variables
export ANTHROPIC_API_KEY=your-key-here
export JWT_SECRET=your-secret-here
# Start all services
docker compose up -dThis starts PostgreSQL, Redis, the API server (port 3001), a database migration runner, and the web frontend (port 80).
# Start PostgreSQL and Redis
docker compose up -d postgres redis
# Install dependencies
cp .env.example .env # Edit with your ANTHROPIC_API_KEY
npm install
# Build shared types
npm run build -w packages/shared
# Push database schema
npx prisma db push --schema apps/api/prisma/schema.prisma
# Start dev servers (in separate terminals)
npm run dev:api # API on http://localhost:3001
npm run dev:web # Frontend on http://localhost:5173The Vite dev server proxies /api requests to the API server.
promptops/ # npm workspaces monorepo
├── packages/shared/ # @promptops/shared — types, enums, Zod schemas
├── apps/api/ # Express 5 + Prisma + BullMQ
│ ├── prisma/schema.prisma # 8 models, 4 enums
│ └── src/
│ ├── routes/ # REST endpoints
│ ├── services/ # Scrapers, analysis, generation, queue
│ ├── lib/ # Prisma, Redis, auth, response helpers
│ ├── middleware/ # Error handler, rate limiter, auth
│ └── utils/ # Claude client, Pino logger
└── apps/web/ # React 19 + Vite + Tailwind v4
└── src/
├── pages/ # Route-level components
├── components/ # UI, layout, feature components
├── hooks/ # Data fetching and mutation hooks
└── lib/api.ts # Fetch wrapper for /api/v1/*
| Layer | Technology |
|---|---|
| Runtime | Node.js >= 20, ESM |
| API | Express 5, Zod validation, Pino logging |
| Database | PostgreSQL 16 via Prisma ORM |
| Queue | BullMQ on Redis 7 |
| AI | Anthropic SDK (Claude Sonnet 4.5) |
| Auth | bcryptjs + jsonwebtoken (Bearer tokens, 7-day expiry) |
| Frontend | React 19, React Router v7, Tailwind CSS v4, Recharts |
| TypeScript | Strict mode, noUncheckedIndexedAccess, verbatimModuleSyntax |
| Variable | Required | Default | Description |
|---|---|---|---|
DATABASE_URL |
Yes | — | PostgreSQL connection string |
REDIS_URL |
No | redis://localhost:6379 |
Redis for BullMQ |
ANTHROPIC_API_KEY |
Yes* | — | Claude API key (*for AI features) |
JWT_SECRET |
No | dev-secret-change-in-production |
Secret for JWT signing |
PORT |
No | 3001 |
API server port |
NODE_ENV |
No | development |
Environment |
LOG_LEVEL |
No | info |
Pino log level |
CORS_ORIGINS |
No | * |
Allowed CORS origins |
SENTRY_DSN |
No | — | Sentry error tracking DSN |
GET /api/v1/health
POST /api/v1/auth/register # { email, password, name? } → { token, user }
POST /api/v1/auth/login # { email, password } → { token, user }
GET /api/v1/auth/me # Current user
GET /api/v1/projects # List (cursor pagination)
POST /api/v1/projects # Create
GET /api/v1/projects/:id # Detail (includes sources, counts)
PATCH /api/v1/projects/:id # Update
DELETE /api/v1/projects/:id # Delete
POST /api/v1/projects/:id/analyze # Trigger AI analysis pipeline
GET /api/v1/sources # List (?projectId= filter)
POST /api/v1/sources # Create
GET /api/v1/sources/:id # Detail
DELETE /api/v1/sources/:id # Delete
POST /api/v1/sources/:id/scrape # Trigger scrape job
GET /api/v1/sources/:id/jobs # Scrape job history
GET /api/v1/insights # List (?projectId=&type=&minSeverity=&tag=)
GET /api/v1/insights/:id # Detail (includes sources + raw posts)
GET /api/v1/specs # List (?projectId=)
POST /api/v1/specs # Create
GET /api/v1/specs/:id # Detail
PATCH /api/v1/specs/:id # Update
DELETE /api/v1/specs/:id # Delete
POST /api/v1/specs/generate # AI spec generation
| Command | Description |
|---|---|
npm run dev:api |
Start API dev server |
npm run dev:web |
Start frontend dev server |
npm run build |
Build shared + API |
npm run build -w apps/web |
Build frontend (tsc + Vite) |
npm run lint |
ESLint |
npm run format |
Prettier (write) |
npm run format:check |
Prettier (check) |
| Command | Description |
|---|---|
npm run db:push -w apps/api |
Push schema to DB (dev) |
npm run db:migrate -w apps/api |
Create migration |
npm run db:generate -w apps/api |
Regenerate Prisma client |
npm run db:studio -w apps/api |
Open Prisma Studio GUI |
npm test # Run all tests (API + web)
npm run test:api # API tests only
npm run test:web # Web tests only- Framework: Vitest 4 with
@testing-library/reactfor component tests - API tests: Integration tests with Prisma and BullMQ mocks
- Web tests: Component/hook tests with mocked API layer
GitHub Actions runs on push/PR to main:
- Lint — ESLint + Prettier check
- Test API — With PostgreSQL 16 and Redis 7 service containers
- Test Web — Component and hook tests with coverage
- Build — Full TypeScript compilation (after lint + tests pass)
- Docker — Build API and web images (main branch only, push events)
Production runs on Railway as a single-service architecture: the API serves both the REST endpoints and the React SPA static files from the same URL.
Live URL: https://promptopsapi-production.up.railway.app
Test account: test@promptops.dev / Test1234
| Service | Description |
|---|---|
@promptops/api |
Express API + React SPA (single service, single URL) |
Postgres |
PostgreSQL database (Railway template) |
Redis |
Redis for BullMQ (Railway template) |
railway.tomlat the repo root configures build and start commandsbuild:railwaycompiles shared → API → web; the web build (apps/web/dist) is served by Express viaexpress.static()+ SPA fallback- The frontend uses relative URLs (
/api/v1/*), so API and SPA coexist on the same domain — no reverse proxy needed - On deploy, Prisma migrations run automatically before the server starts
- In development, Vite's dev server proxies
/apitolocalhost:3001for the same effect
| Variable | Description |
|---|---|
DATABASE_URL |
Internal Railway Postgres URL |
REDIS_URL |
Internal Railway Redis URL |
ANTHROPIC_API_KEY |
Claude API key |
JWT_SECRET |
32+ chars in production |
NODE_ENV |
production |
CORS_ORIGINS |
Railway public domain URL |
PORT |
3001 |
| Service | Image | Port | Description |
|---|---|---|---|
postgres |
postgres:16 | 5432 | PostgreSQL database |
redis |
redis:7 | 6379 | Redis for BullMQ job queue |
api |
promptops-api | 3001 | Express API + BullMQ worker + SPA |
migrate |
promptops-api | — | One-shot Prisma migration runner |
ISC