Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[PG] Add Support For Domains and Check Constraints On Columns #4040

Open
wants to merge 26 commits into
base: main
Choose a base branch
from

Conversation

anthonyalayo
Copy link

@anthonyalayo anthonyalayo commented Jan 30, 2025

This PR introduces PostgreSQL Domain Support and enhances Check Constraint Integration with Drizzle-Zod, addressing issues #218, #3446, and #3520.

Why this PR?

End the Validation Duplication Nightmare

Tired of maintaining duplicate validation logic? This PR eliminates the need to write the same rules in two places:

  1. Database constraints (CHECK clauses, domains)

  2. Application validation (Zod schemas, manual checks)

Before 😩

// Database schema
users = pgTable('users', {
  email: text('email').check(sql`email ~ '^[^@]+@[^@]+\.[^@]+'`)
});

// Zod schema (manual duplication!)
const UserSchema = z.object({
  email: z.string().email()
});

Now 🎉

// Define validation ONCE at the data layer
const emailDomain = pgDomain('email', 'text', {
  checkConstraints: [
    check('valid_email', sql`value ~ '^[^@]+@[^@]+\.[^@]+'`)
  ]
});

// Zod automatically inherits database constraints! ✅
const UserSchema = createSelectSchema(users);
// Result: z.string().regex(emailRegex)

Key Benefits 🌟

1. Single Source of Truth
Define validation rules once in your database schema - Drizzle automatically propagates them to Zod

2. DRY Principle Enforced
Never accidentally update application validation without updating database constraints (or vice versa)

3. Type Safety from Edge to Edge
Get Zod schemas that exactly match your database constraints, from API endpoints to UI forms

4. Schema Evolution Made Safe
Changing a constraint in your migration? Zod validations automatically update when you regenerate types

Real-World Impact 💸

For a medium-sized app with:

  • 50 database tables
  • 3-5 constraints per table
  • 10+ API endpoints

You'll eliminate 200-300 lines of manual Zod validation code and save 5-10 hours/month on schema maintenance.

Changes: PostgreSQL Domain Support

What are Domains?

Domains are user-defined data types that enforce constraints on existing PostgreSQL types. They help standardize column definitions and reuse validation rules across tables.

Defining Domains in Drizzle

import { pgDomain } from 'drizzle-orm/pg-core';

// Create a domain for email validation
const emailDomain = pgDomain('email', 'text', {
  notNull: true,
  checkConstraints: [
    check('valid_email', sql`value ~ '^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$'`)
  ]
});

// Use domain in table definition
const users = pgTable('users', {
  id: serial('id').primaryKey(),
  email: emailDomain('email')  // Applies domain constraints
});

Equivalent SQL:

CREATE DOMAIN email AS text 
CONSTRAINT valid_email CHECK (value ~ '^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$');

Key Features

  • Full migration support for domain creation/alteration
  • Domain-aware schema introspection
  • Cross-schema domain support
  • Automatic constraint validation during inserts/updates

Changes: Check Constraint Integration with Drizzle-Zod

Automatic Zod Validation

Check constraints (both column-level and domain-based) now generate proper Zod validations:

// Table definition with check constraint
const users = pgTable('users', {
  id: serial('id').primaryKey(),
  username: text('username')
    .checkConstraint(check('username_length', sql`length(username) BETWEEN 3 AND 20`))
});

// Generated Zod schema will include:
// z.string().min(3).max(20)
const userSchema = createSelectSchema(users);

Supported Constraints

  • Minimum/maximum values (integers, bigints, dates)
  • String length limits
  • Regular expression patterns
  • Range checks (BETWEEN)
  • Custom SQL expressions

TODOs

  • handle unnamed constraints

@anthonyalayo
Copy link
Author

@AndriiSherman @dankochetov mind looking at this PR? Domain support was on the backlog for almost 2 years. I really want it :-) so I gave it a shot myself.

@AndriiSherman AndriiSherman self-requested a review January 30, 2025 07:51
@AndriiSherman
Copy link
Member

Awesome! Will definitely check it

@anthonyalayo anthonyalayo changed the title [PG] Add Support For Domains [PG] Add Support For Domains and Check Constraints On Columns Jan 31, 2025
@Tpgainz
Copy link

Tpgainz commented Feb 3, 2025

Cmon man that rocks

@anthonyalayo
Copy link
Author

@AndriiSherman I put in all the remaining changes that I can foresee. There's a lot of new tests that hopefully cover everything. Would you mind giving it a review? Feel free to change anything you like. It would be fantastic if this could get into V1.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants