Architecture & Tech Stack
Technical overview for developers
Tech Stack#
Askify is built with modern, production-ready technologies:
- Runtime: Node.js 22+
- Framework: Bolt for JavaScript (@slack/bolt v4)
- Database: PostgreSQL (via Supabase)
- ORM: Prisma v7 with @prisma/adapter-pg
- Scheduler: node-cron for background jobs
- Deployment: Docker with multi-stage builds
Database Schema#
Askify uses four main database models:
Poll
Stores poll metadata and configuration.
- UUID primary key
- Creator ID, channel ID, message timestamp
- Question text
- Poll type (ENUM: single_choice, multi_select, yes_no, rating)
- Settings (JSONB: anonymous, vote change, live results, reminders)
- Status (ENUM: draft, scheduled, active, closed)
- Scheduled and close timestamps
PollOption
Stores individual poll options.
- UUID primary key
- Foreign key to Poll
- Label text (max 200 chars)
- Position (display order)
- Added by (user ID if voter-added)
Vote
Tracks individual votes.
- UUID primary key
- Foreign keys to Poll and PollOption
- Voter ID (Slack user ID)
- Voted at timestamp
PollTemplate
Stores saved poll configurations.
- UUID primary key
- User ID (template owner)
- Template name
- Config (JSONB: full poll configuration)
- Created at timestamp
Architecture Overview#
Askify follows an event-driven architecture with these key components:
Slash Commands
Entry points for user interactions (src/commands/):
- /askify - Opens poll creation modal
- /askify poll - Inline quick poll creation
- /askify list - Shows user's polls with filtering
- /askify templates - Manages poll templates
- /askify help - Displays usage guide
Actions
Handle button clicks and select menus (src/actions/):
- Vote buttons (regex: /^vote_.+$/)
- Close poll, add option, save template buttons
- Modal select menus with dispatch_action
- List actions (close, cancel, results)
Views
Process modal submissions (src/views/):
- Poll creation modal
- Template save modal
- Add option modal
- Share results modal
Background Jobs
Scheduled tasks using node-cron (src/jobs/):
- autoCloseJob - Closes expired polls (every minute)
- scheduledPollJob - Posts scheduled polls (every minute)
- reminderJob - Sends reminder DMs (every 15 minutes)
- startupRecovery - Catches up on missed events (on startup)
Services
Business logic and database operations (src/services/):
- pollService - CRUD operations for polls
- voteService - Vote processing and deduplication
- templateService - Template management
Event Flow#
Typical poll lifecycle:
- User runs /askify → Slash command handler opens modal
- User submits modal → View handler validates and creates poll in database
- If scheduled: Job picks it up at scheduled time and posts
- If immediate: Bot posts poll message via chat.postMessage
- Voters click buttons → Action handler records votes, updates message
- Vote updates debounced (500ms) to batch rapid clicks
- Auto-close job checks periodically, closes expired polls
- On close: Results DM sent to creator, voting disabled
- Creator clicks 'Share Results' → Posts to chosen channel
Key Patterns#
Socket Mode
Askify uses Slack Socket Mode for real-time communication without exposing a public endpoint. This eliminates the need for ngrok or public URLs during development.
Debounced Updates
Rapid vote clicks are debounced per poll (500ms). The debounced callback refetches fresh data, ensuring the final update reflects current state.
Dynamic Modals
Modal select elements with dispatch_action: true trigger handlers that rebuild and update the modal via client.views.update().
Retry Logic
Exponential backoff for Slack API rate limits using withRetry() wrapper (src/utils/slackRetry.ts).
Project Structure#
src/
app.ts # Entry point
lib/prisma.ts # Prisma singleton
commands/ # Slash command handlers
actions/ # Button/select handlers
views/ # Modal submissions
events/ # Event handlers (DM, App Home)
middleware/ # Request logger
services/ # Business logic
blocks/ # Block Kit builders
jobs/ # Background jobs
utils/ # Helpers
prisma/
schema.prisma # Database schemaDevelopment#
Common development commands:
# Development
npm run dev # Hot-reload with nodemon
# Build
npm run build # Compile TypeScript
npm start # Run compiled app
# Database
npm run prisma:migrate # Create/apply migrations
npm run prisma:generate # Regenerate Prisma Client
npm run prisma:studio # Open database browser
# Type checking
npx tsc --noEmit # Check types without buildingDeployment#
Askify is designed for self-hosting. Deployment options:
- Docker: Multi-stage Dockerfile included
- VPS: Deploy to any server with Node.js and PostgreSQL
- PM2: Process manager for production
- GitHub Actions: Automated deployment pipeline
See docs/DEPLOYMENT.md in the repository for detailed instructions.