Early-stage OSS · pin a version in 0.x

Latency budgets for the SQL your Node app actually runs.

queryd wraps the SQL your app actually runs — tagged templates (e.g. postgres.js), string SQL (wrapQueryFn), or Prisma via @olegkoval/queryd/prisma — with per-query latency thresholds, optional per-requestId query budgets, sampling, optional EXPLAIN ANALYZE, and pluggable sinks.

Budgets, not vibes

Flag database work that exceeds a latency threshold so regressions surface before users feel them.

Driver-agnostic core

wrapTaggedTemplate and wrapQueryFn instrument real clients; Prisma lives on @olegkoval/queryd/prisma so the base package stays lean.

Predictable overhead

Sampling-first design so observability stays cheap enough to leave on in production.

Explain the worst

Optional EXPLAIN pipeline to inspect the slowest offenders when you need depth, not noise.

Usage · Prisma
TypeScript
import { PrismaClient } from "@prisma/client";
import {
  createSlowQueryDetector,
  createConsoleLogger,
  runWithDbContext,
} from "@olegkoval/queryd";
import { wrapPrismaClient } from "@olegkoval/queryd/prisma";

const base = new PrismaClient();
const detector = createSlowQueryDetector(
  {
    warnThresholdMs: 200,
    dbName: "primary",
    requestBudget: { maxQueries: 80, maxTotalDurationMs: 2000 },
  },
  { logger: createConsoleLogger() },
);
export const prisma = wrapPrismaClient(base, detector);

await runWithDbContext({ requestId: "req-1", userId: "u-1" }, async () => {
  await prisma.$queryRaw`SELECT 1`;
});
Usage · postgres.js
TypeScript
import postgres from "postgres";
import {
  createSlowQueryDetector,
  wrapTaggedTemplate,
  createConsoleLogger,
  runWithDbContext,
} from "@olegkoval/queryd";

const sql = postgres(process.env.DATABASE_URL!);
const detector = createSlowQueryDetector(
  {
    warnThresholdMs: 200,
    dbName: "primary",
    requestBudget: { maxQueries: 80, maxTotalDurationMs: 2000 },
  },
  { logger: createConsoleLogger() },
);
const instrumentedSql = wrapTaggedTemplate(sql, detector);

await runWithDbContext({ requestId: "req-abc" }, async () => {
  await instrumentedSql`select ${1}::int`;
});
Usage · wrapQueryFn
TypeScript
import {
  createSlowQueryDetector,
  wrapQueryFn,
  createConsoleLogger,
  runWithDbContext,
} from "@olegkoval/queryd";

const rawQuery = async (sql: string, params: unknown[]) => {
  /* your client */
  return [];
};
const detector = createSlowQueryDetector(
  { warnThresholdMs: 200, requestBudget: { maxQueries: 50 } },
  { logger: createConsoleLogger() },
);
const q = wrapQueryFn(rawQuery, detector);

await runWithDbContext({ requestId: "job-7" }, async () => {
  await q("select 1", []);
});

Request budgets: LRU eviction resets per-requestId counters (same id string can violate again after re-insertion). createSlowQueryDetector may push onto your sinks array — see README.

MIT licensed · oleg-koval/slow-query-detector · package name on npm is @olegkoval/queryd

Listed on Sell With boost