We've been doing this backwards for decades.
You define your data model in SQL. Then you write a GraphQL schema. Then TypeScript interfaces. Then Zod validators. Then API docs. Five different representations of the exact same data structure, maintained by hand, drifting apart with every sprint.
One missed migration and suddenly your frontend expects a field that doesn't exist. One mistyped interface and runtime errors cascade through production. We've normalized this insanity as "just how web development works."
But what if we inverted the entire paradigm?
GraphQL Isn't Just an API Layer
GraphQL already expresses your data model better than SQL. It has types, relationships, non-nullability, and documentation built in. It's human-readable. It evolves gracefully. Most importantly: it's how developers actually think about data.
So why are we still treating SQL as the source of truth and GraphQL as the derived artifact?
Wesley flips the script. Write your schema in GraphQL once, augmented with semantic directives, and generate your entire data layer: Postgres DDL, TypeScript types, Zod validators, pgTAP tests, and safe migrations.
One schema. Everything generated. Zero drift. Migrations for free.
How It Works
Wesley extends GraphQL with intuitive directives that tell it how to translate your domain model into working infrastructure:
type Organization @wes_table {
id: ID! @wes_pk
name: String! @wes_unique
slug: String! @wes_unique @wes_index
created_at: DateTime! @wes_default(value: "now()")
}
type User @wes_table @wes_tenant(by: "org_id") {
id: ID! @wes_pk @wes_default(value: "gen_random_uuid()")
org_id: ID! @wes_fk(ref: "Organization.id")
email: String! @wes_unique @wes_index
password_hash: String! @wes_sensitive
created_at: DateTime! @wes_default(value: "now()")
updated_at: DateTime! @wes_default(value: "now()")
}
From this single schema, Wesley compiles:
PostgreSQL DDL with proper constraints, indexes, and foreign keys
CREATE TABLE organizations (
id UUID PRIMARY KEY,
name TEXT UNIQUE NOT NULL,
slug TEXT UNIQUE NOT NULL,
created_at TIMESTAMPTZ DEFAULT now() NOT NULL
);
CREATE INDEX idx_organizations_slug ON organizations(slug);
CREATE TABLE users (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
org_id UUID NOT NULL REFERENCES organizations(id),
email TEXT UNIQUE NOT NULL,
password_hash TEXT NOT NULL,
created_at TIMESTAMPTZ DEFAULT now() NOT NULL,
updated_at TIMESTAMPTZ DEFAULT now() NOT NULL
);
TypeScript types that stay in sync automatically
interface Organization {
id: string;
name: string;
slug: string;
created_at: Date;
}
interface User {
id: string;
org_id: string;
email: string;
password_hash: string;
created_at: Date;
updated_at: Date;
}
Zod validators for runtime safety
const UserSchema = z.object({
id: z.string().uuid(),
org_id: z.string().uuid(),
email: z.string().email(),
password_hash: z.string().min(1),
created_at: z.date(),
updated_at: z.date(),
});
pgTAP tests to verify your schema
SELECT has_table('users');
SELECT has_column('users', 'email');
SELECT col_is_unique('users', 'email');
SELECT has_fk('users', 'org_id', 'organizations', 'id');
And most importantly: safe, phased migrations. Wesley generates expand → backfill → validate → switch → contract migrations that minimize downtime and lock contention.
Why It's Safe to Ship
Every Wesley compilation produces a SHA-locked evidence bundle, a cryptographic proof that your changes are safe. The verification system analyzes:
- Schema coverage and completeness
- Migration risk assessment
- Test confidence metrics
- Historical deployment success
Before your migrations touch production, they're rehearsed in an isolated shadow environment. The system captures lock levels, timing data, and potential failure modes. Only verified, tested changes get deployed.
This isn't "trust me bro, it'll probably work." It's cryptographically verifiable proof that your migration was validated. Deploy database changes on Friday with confidence.
Row-Level Security Built In
Multi-tenant applications need data isolation. With the @wes_tenant directive, Wesley generates complete RLS policies automatically:
type Document @wes_table @wes_tenant(by: "org_id") {
id: ID! @wes_pk
org_id: ID! @wes_fk(ref: "Organization.id")
title: String!
content: String!
}
Wesley guarantees users can only access their organization’s data, and brings the pgTAP tests to demonstrate it. No manual policy writing. No forgotten WHERE clauses. Security by default.
The Paradigm Shift
The revolution isn't the code generation. It's the inversion of thinking.
Everyone assumes SQL comes first because "that's where the data lives." But data doesn't live in SQL. It lives in our mental models. GraphQL expresses those models better than SQL ever could.
Wesley makes the computer work the way our brains work, not the other way around.
What's Next
Wesley is open source and ready for early adopters:
- GitHub Repository - Source code and documentation
- 5-Minute Quickstart - Try it out!
- Architecture Deep-Dive - Read the full paradigm shift explanation
- Join the Discussion - Share your use cases
Wesley is just getting started. The roadmap includes:
- Visual schema editor for designing your data model
- Time-travel debugging through schema evolution
- Multi-database support (MySQL, SQLite, and more)
- Framework-specific plugins (Next.js, Remix, SvelteKit)
- AI-powered schema optimization suggestions
- and more...
The future is schema-first. The future is Wesley.
Go on then. Deploy on a Friday. I dare you.
Wesley is named after Wesley Crusher from Star Trek: The Next Generation, the brilliant ensign who saw possibilities others couldn't. Like his namesake, Wesley (the tool) transcends conventional thinking to solve problems in ways that seem obvious only in hindsight.



Top comments (0)