PromptLab is a personal prompt management app for storing prompts, versioning them, and running prompt variants against OpenRouter-backed models.
- Backend: Django 5, Django REST Framework, SimpleJWT
- Frontend: Vue 3, Vite, Pinia, Tailwind CSS
- Database: PostgreSQL via Docker Compose, or SQLite for local backend-only development
- LLM provider: OpenRouter
- Allows each user to create and organize their own personal library of AI prompts
- Automatically tracks versions of prompts
- Enables you to run and test prompts with dynamic variables and compare results
- Keeps a history of all your prompt runs, including performance, token usage, and cost details
- Lets you securely manage your own OpenRouter API key for private access
- Supports customizing and saving your favorite AI models to try out with your prompts
backend/ Django API and data model
frontend/ Vue SPA
User
- id (UUID)
- email (unique)
- full_name
- openrouter_api_key (encrypted at rest, nullable)
- created_at
- updated_at
Prompt
- id (UUID)
- owner (FK -> User)
- name
- description
- created_by (FK -> User, nullable)
- latest_version (FK -> PromptVersion, nullable)
- created_at
- updated_at
PromptVersion
- id (UUID)
- prompt (FK -> Prompt)
- version_number (auto-incremented per prompt)
- system_message
- user_message_template
- model_config (JSON)
- created_by (FK -> User, nullable)
- created_at
PromptRun
- id (UUID)
- prompt_version (FK -> PromptVersion)
- executed_by (FK -> User, nullable)
- model_id
- resolved_model_id
- variables (JSON)
- response_text
- input_tokens
- output_tokens
- total_cost_usd
- latency_ms
- error_message
- created_at
- updated_at
Note: custom model IDs are not stored in the backend. The Settings view saves them in browser localStorage, scoped by user ID.
All frontend requests are made under /api.
POST /api/auth/register/POST /api/auth/login/POST /api/auth/refresh/POST /api/auth/logout/GET /api/auth/me/
GET /api/settings/PUT /api/settings/api-key/
GET /api/settings/ returns account info plus has_api_key. It does not return the raw API key.
GET /api/prompts/POST /api/prompts/GET /api/prompts/{prompt_id}/PATCH /api/prompts/{prompt_id}/DELETE /api/prompts/{prompt_id}/GET /api/prompts/{prompt_id}/versions/POST /api/prompts/{prompt_id}/versions/
GET /api/prompts/ supports an optional search query parameter.
POST /api/prompts/{prompt_id}/run/GET /api/runs/GET /api/runs/{run_id}/
GET /api/runs/ supports:
prompt_idpage(default1)page_size(default20, max100)
GET /api/models/
The backend currently exposes one built-in option:
openrouter/free
The frontend merges this list with user-added custom model IDs from localStorage.
//login/register/prompts/prompts/:id/prompts/:id/edit/playground/runs/settings
SECRET_KEYDJANGO_DEBUGDATABASE_URLFERNET_KEYALLOWED_HOSTSCORS_ALLOWED_ORIGINSCSRF_TRUSTED_ORIGINS
If DATABASE_URL is not set, Django falls back to backend/db_dev.sqlite3.
When DJANGO_DEBUG=False, SECRET_KEY, FERNET_KEY, and ALLOWED_HOSTS are required. The app only falls back to a derived Fernet key during local development.
- No runtime environment variables are required for the production image.
VITE_API_BASE_URLis only used by the Vite development proxy.
- Access tokens are kept in browser memory only.
- Refresh tokens are stored in an
HttpOnlycookie. - Django security headers, HTTPS redirects, HSTS, and secure cookies are enabled automatically when
DJANGO_DEBUG=False. - DRF throttling is enabled for auth endpoints and prompt execution.
cp .env.example .env
docker compose up --buildServices:
- frontend + API: http://localhost
- backend: internal only
- postgres: internal only
The provided Compose stack builds the frontend once, serves it with Nginx, proxies /api/* to Django, and runs Django with gunicorn.
For frontend-only development with the Vite proxy:
cd frontend
npm install
npm run dev- Each user provides their own OpenRouter API key in Settings.
- The key is encrypted before being stored in the database.