npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

drizzle-validator

v0.0.1

Published

Declarative async validation for Drizzle ORM

Readme

drizzle-validator

Declarative async validation for Drizzle ORM — the checks a normal schema validator can't do because they need a query: "does this category exist?", "is this email already taken?".

You point a rule at a Drizzle column, pass it your request body, and get back a plain result. The library never throws — you decide what an invalid result means. Works on Postgres, MySQL and SQLite.

import { validate, exists, unique, existsAll } from 'drizzle-validator';

const result = await validate(db, body, {
  email:       unique(users.email),
  categoryId:  exists(categories.id),
  tagIds:      existsAll(tags.id),
});

if (!result.res) {
  console.log(result.errors); // [{ key, message, rule, table, column?, value? }]
}

Install

npm install drizzle-validator

How it reads

  • validate(db, body, rules)db is your Drizzle client, body is the object you're validating (e.g. a request body), and rules is keyed by field name.
  • Each rule takes a Drizzle column. The column tells the rule what table to query and what type the field must be — so pointing a rule at the wrong column is a TypeScript error.
  • A field whose value is undefined is skipped (handy for optional/partial updates).
  • You get back either { res: true } or { res: false, errors: [...] }.

The rules

exists(column)

Passes when a row with that value exists.

await validate(db, body, { categoryId: exists(categories.id) });

unique(column)

Passes when no row has that value. Use it for "email already taken" style checks.

await validate(db, body, { email: unique(users.email) });

On update, exclude the record itself so it doesn't clash with its own value:

await validate(db, body, {
  email: unique(users.email).exclude(users.id, currentUserId),
});

existsAll(column)

For an array field — passes when every element exists. Great for many-to-many links (e.g. attaching a list of tags). An empty array passes.

await validate(db, body, { tagIds: existsAll(tags.id) });

Chaining rules on one field

Give a field an array of rules. They run in order and stop at the first failure — so you don't get a confusing "not allowed" error for an id that simply doesn't exist:

await validate(db, body, {
  categoryId: [ exists(categories.id), /* ...more checks... */ ],
});

Different fields are checked in parallel; you get one error per failing field.

The result

type ValidationResult =
  | { res: true }
  | { res: false; errors: ValidationFailure[] };

type ValidationFailure = {
  key: string;      // the field that failed
  message: string;  // human-readable
  rule: string;     // 'exists' | 'unique' | 'existsAll'
  table: string;    // the DB table checked
  column?: string;  // the column checked
  value?: unknown;  // the offending value (for existsAll, the missing items)
};

Because failures are structured, you can render whatever error shape your app needs instead of parsing strings.

Throwing, if you want it

The library returns; you throw. A common pattern in a web framework:

const result = await validate(db, body, rules);

if (!result.res) {
  throw new UnprocessableEntityError(result.errors); // your framework's 422
}

Validating in the same place a lot? Bind the db once:

import { createValidator } from 'drizzle-validator';

const validator = createValidator(db);
const result = await validator.validate(body, rules);

License

MIT