sql-slow
v2.0.0
Published
Ultra Pro SQL slow-query detector with AI optimization, multi-channel alerts, real-time dashboard and deep analytics
Maintainers
Readme
sql-slow v2 — Ultra Pro Slow Query Detector
Zero-config slow-query monitoring for Node.js with AI-powered optimization, multi-channel alerts (Telegram · Slack · Discord · Webhook), and a real-time dark dashboard.
Table of Contents
- Why sql-slow?
- Quick Start
- Installation
- Configuration Reference
- Database Adapters
- AI Optimization
- Alert Channels
- Real-Time Dashboard
- Events API
- Manual Ingestion
- Architecture
- Pricing
- License
Why sql-slow?
Even a single un-indexed query on a table with millions of rows can:
- Spike CPU on your database server
- Block all other queries from executing
- Take down Postgres / MySQL for minutes
- Cause customer-facing downtime and revenue loss
Traditional approaches require you to grep logs manually, set up slow-query log on the DB server, and write your own alerting. sql-slow intercepts every query at the ORM/driver level, detects the slow ones in real time, calls an AI to suggest a fix, and sends you an alert in seconds.
Quick Start
import { PrismaClient } from '@prisma/client';
import SqlSlow from 'sql-slow';
// Prisma must be created with query logging enabled
const prisma = new PrismaClient({
log: [{ level: 'query', emit: 'event' }],
});
// One-liner: create + start
const sq = SqlSlow.create(prisma, {
alert: '>500ms', // fire alert when query > 500 ms
fix: 'ai', // auto-detect AI provider from env vars
appName: 'My API',
telegram: {
botToken: process.env.TELEGRAM_BOT_TOKEN!,
chatId: process.env.TELEGRAM_CHAT_ID!,
},
dashboard: true, // http://localhost:4001
});
// Your app runs normally – all Prisma queries are monitored automatically
const users = await prisma.user.findMany({ where: { active: true } });
// If this takes > 500 ms you'll get a Telegram message with an AI fix suggestionInstallation
npm install sql-slowSet environment variables (copy .env.example):
# Required for AI suggestions (pick one)
OPENAI_API_KEY=sk-...
# ANTHROPIC_API_KEY=sk-ant-...
# GEMINI_API_KEY=...
# Required for Telegram alerts
TELEGRAM_BOT_TOKEN=123456:ABC...
TELEGRAM_CHAT_ID=-100123456789
# Optional
SLACK_WEBHOOK_URL=https://hooks.slack.com/services/...
DISCORD_WEBHOOK_URL=https://discord.com/api/webhooks/...
SQL_SLOW_DASHBOARD_PORT=4001
SQL_SLOW_DASHBOARD_SECRET=my_secretConfiguration Reference
const sq = SqlSlow.create(prisma, {
// ── Threshold ──────────────────────────────────────────────────
alert: '>500ms', // Supports: ">500ms" | ">=1s" | ">2000" (no unit = ms)
// ── AI ─────────────────────────────────────────────────────────
fix: 'ai', // 'ai' = auto-detect provider from env vars
// OR full control:
fix: {
provider: 'openai', // 'openai' | 'anthropic' | 'gemini'
apiKey: 'sk-...',
model: 'gpt-4o-mini',
maxTokens: 1500,
temperature: 0.3,
timeoutMs: 30_000,
includeQueryInPrompt: true, // set false to mask literals
},
// ── Alerts ─────────────────────────────────────────────────────
consoleAlerts: true, // always print to stdout
telegram: {
botToken: 'BOT_TOKEN',
chatId: 'CHAT_ID',
messageThreadId: 123, // optional – Topics
parseMode: 'HTML', // 'HTML' | 'Markdown'
},
slack: {
webhookUrl: 'https://hooks.slack.com/...',
channel: '#db-alerts',
username: 'sql-slow',
iconEmoji: ':snail:',
},
discord: {
webhookUrl: 'https://discord.com/api/webhooks/...',
username: 'sql-slow',
},
webhook: {
url: 'https://your-api.com/alerts',
method: 'POST',
headers: { 'X-Api-Key': 'secret' },
secret: 'bearer-token',
},
// ── Dashboard ──────────────────────────────────────────────────
dashboard: {
enabled: true,
port: 4001,
host: '0.0.0.0',
secret: 'my-secret', // bearer token to protect API
maxStoredQueries: 5000,
},
// ── Advanced ───────────────────────────────────────────────────
adapter: 'auto', // 'auto' | 'prisma' | 'pg' | 'mysql2'
sampleRate: 1.0, // 0.0–1.0 fraction of queries to analyse
maxConcurrentAI: 3, // max parallel AI requests
dailyReportHour: 8, // send daily report at 08:00 UTC (-1 = off)
logLevel: 'info', // 'debug' | 'info' | 'warn' | 'error' | 'silent'
appName: 'My API',
});Database Adapters
Prisma
// ⚠️ MUST enable query event emission
const prisma = new PrismaClient({
log: [{ level: 'query', emit: 'event' }],
});
SqlSlow.create(prisma, { alert: '>500ms' });node-postgres (pg)
import { Pool } from 'pg';
const pool = new Pool({ connectionString: process.env.DATABASE_URL });
SqlSlow.create(pool, { alert: '>500ms' });mysql2
import mysql from 'mysql2';
const pool = mysql.createPool({ host: 'localhost', database: 'mydb' });
SqlSlow.create(pool, { alert: '>500ms' });Manual adapter (any ORM)
const sq = new SqlSlow(null, { alert: '>300ms' });
sq.start();
// Call sq.ingest() around your queries
const start = Date.now();
const result = await myDb.query('SELECT ...');
sq.ingest('SELECT ...', Date.now() - start);AI Optimization
When a slow query is detected sql-slow calls your configured AI provider and returns:
| Field | Description |
|-------|-------------|
| issue | Short root-cause label |
| suggestion | Human-readable explanation |
| code | Optimized SQL or migration DDL |
| expectedImprovement | Narrative estimate |
| estimatedGainPct | 0-100 percentage estimate |
| indexRecommendations | Array of CREATE INDEX DDL statements |
| queryRewrites | Alternative SQL rewrites |
| severity | critical / high / medium / low |
Results are LRU-cached (1 hour TTL, 500 entries) so structurally identical queries only call the AI once.
// One-off analysis without monitoring
const result = await sq.analyzeQuery(
'SELECT * FROM orders WHERE user_id = 1',
1200 // ms
);
console.log(result?.suggestion);Alert Channels
Telegram alert example
🟡 Slow Query Detected
App: My API
Duration: 1.23s (threshold: 500ms)
Table: orders
Operation: SELECT
Severity: WARNING
Time: 16/06/2026 14:22:05 UTC
SQL:
SELECT * FROM orders WHERE user_id = 1 ORDER BY created_at DESC
💡 AI Suggestion
Issue: Missing composite index on orders(user_id, created_at)
Fix: Add a composite index to cover the WHERE + ORDER BY.
Expected gain: 70-80% reduction in execution time
📌 Index Recommendations:
CREATE INDEX idx_orders_user_created ON orders(user_id, created_at DESC);Test all channels
const results = await sq.testAlerts();
// { telegram: true, slack: false, discord: true }Real-Time Dashboard
Enable with dashboard: true (or dashboard: { port: 4001 }).
Visit http://localhost:4001 to see:
- KPI cards – Total / Slow / Critical queries, Avg duration
- Percentiles – P50 · P75 · P90 · P95 · P99 · Max
- Operation chart – SELECT / INSERT / UPDATE / DELETE breakdown (Doughnut)
- Top tables – Most-hit tables (Bar chart)
- Slowest queries table – Live, filterable, click-to-copy SQL
- Auto-refresh every 4 seconds
Protect the dashboard with a bearer token:
dashboard: { enabled: true, secret: 'my-secret' }Then access /api/stats with Authorization: Bearer my-secret.
Events API
const sq = new SqlSlow(prisma, { alert: '>200ms' });
// Every query
sq.on('query', (metrics) => {
console.log(metrics.duration, metrics.table);
});
// Only slow queries
sq.on('slow-query', ({ metrics, thresholdMs }) => {
console.log(`${metrics.duration}ms > ${thresholdMs}ms`);
});
// Critical (≥3× threshold)
sq.on('critical-query', ({ metrics }) => {
pagerDuty.trigger(metrics);
});
sq.start();Manual Ingestion
const sq = new SqlSlow(null, { alert: '>300ms' });
sq.start();
// Wrap any database call
const t0 = Date.now();
await knex('users').where({ active: true });
sq.ingest('SELECT * FROM users WHERE active = true', Date.now() - t0);Architecture
SqlSlow (src/index.ts)
│
├── SqlSlowMonitor (src/monitor.ts)
│ ├── MetricsStore (src/utils/metrics.ts) ← in-memory LRU store
│ ├── EventEmitter3 ← query / slow-query / critical-query
│ └── Adapters (src/adapters/)
│ ├── prismaAdapter.ts → prisma.$on('query')
│ ├── pgAdapter.ts → pool.query() monkey-patch
│ └── mysql2Adapter.ts → connection.query() monkey-patch
│
├── AIOptimizer (src/ai/optimizer.ts)
│ ├── AiCache (src/ai/cache.ts) ← LRU + TTL
│ └── Providers
│ ├── openai.ts → GPT-4o-mini / GPT-4
│ ├── anthropic.ts → Claude 3 Haiku / Sonnet
│ └── gemini.ts → Gemini 1.5 Flash / Pro
│
├── AlertService (src/alerts/alertService.ts)
│ ├── TelegramChannel → bot.sendMessage (HTML)
│ ├── SlackChannel → Incoming Webhook (Block Kit)
│ ├── DiscordChannel → Webhook (Embeds)
│ ├── WebhookChannel → Generic HTTP POST
│ └── Console → ANSI-coloured stdout
│
└── DashboardServer (src/dashboard/server.ts)
├── GET / → Self-contained dark SPA (Chart.js + Tailwind CDN)
├── GET /api/stats → Full stats JSON
├── GET /api/slow-queries
├── GET /api/top-slowest
├── GET /health
└── DELETE /api/metricsPricing
| Tier | Price | Limits | |------|-------|--------| | Free | $0 | 10 000 queries/month · Telegram alerts · Basic stats | | Pro | $34/mo | Unlimited · Dashboard · AI suggestions · Daily reports · Priority support | | Enterprise | Custom | White-label · SLA · Custom integrations · 24/7 support |
License
MIT © sql-slow contributors
