惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Vercel News
Vercel News
Recorded Future
Recorded Future
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
The GitHub Blog
The GitHub Blog
Application and Cybersecurity Blog
Application and Cybersecurity Blog
Google DeepMind News
Google DeepMind News
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
Microsoft Azure Blog
Microsoft Azure Blog
K
KPMG report finds enterprise disconnect between AI and its ROI | CIO
M
MIT News - Artificial intelligence
云风的 BLOG
云风的 BLOG
Y
Y Combinator Blog
N
News | PayPal Newsroom
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
Help Net Security
Help Net Security
博客园 - Franky
SecWiki News
SecWiki News
Recent Announcements
Recent Announcements
T
Troy Hunt's Blog
The Register - Security
The Register - Security
The Last Watchdog
The Last Watchdog
Webroot Blog
Webroot Blog
S
Security Affairs
博客园 - 司徒正美
S
Schneier on Security
I
InfoQ
博客园_首页
www.infosecurity-magazine.com
www.infosecurity-magazine.com
T
Threat Research - Cisco Blogs
Forbes - Security
Forbes - Security
腾讯CDC
N
Netflix TechBlog - Medium
N
News and Events Feed by Topic
Cloudbric
Cloudbric
T
The Exploit Database - CXSecurity.com
P
Proofpoint News Feed
A
About on SuperTechFans
Engineering at Meta
Engineering at Meta
Recent Commits to openclaw:main
Recent Commits to openclaw:main
B
Blog
V
Vulnerabilities – Threatpost
C
Check Point Blog
Google DeepMind News
Google DeepMind News
Google Online Security Blog
Google Online Security Blog
C
Cyber Attacks, Cyber Crime and Cyber Security
Hacker News - Newest:
Hacker News - Newest: "LLM"
C
Cisco Blogs
Schneier on Security
Schneier on Security
O
OpenAI News
K
Kaspersky official blog

DEV Community

Authentication Security Deep Dive: From Brute Force to Salted Hashing (With Java Examples) Why AI Systems Don’t Fail — They Drift Spilling beans for how i learn for exam😁"Reinforcement Learning Cheat Sheet" I Replaced Chrome with Safari for AI Browser Automation. Here's What Broke (and What Finally Worked) How Python Borrows Other People's Work The $40 Architecture: Processing 1 Billion API Requests with 99.99% Uptime Vibe Coding: A Workflow Guide (From Zero to SaaS) Most webhook security guides protect the wrong side. The scary part is delivery. Headless CMS for TanStack Start: Build a Blog with Cosmic EU Age Verification App "Hacked in 2 Minutes" — What Actually Happened Comfy Cloud’s delete function does not actually remove files Running AI Models on GPU Cloud Servers: A Beginner Guide Event-driven media intelligence with AWS Step Functions and Bedrock I scored 500 AI prompts across 8 quality dimensions — here's what broke How to Call Google Gemini API from Next.js (Free Tier, No Backend Needed) The Portal Protocol: Reclaiming Human Connection in the Age of AI How to Fix Your Team's Scattered Knowledge Problem With a Self-Hosted Forum Intro to tc Cloud Functors: A Graph-First Mental Model for the Modern Cloud Designing Multi-Tenant Backends With Both Ownership and Team Access I Built a Neumorphic CSS Library with 77+ Components — Here's What I Learned PostgreSQL Performance Optimization: Why Connection Pooling Is Critical at Scale Cómo construí un SaaS multi-rubro para gestionar expensas en Argentina con FastAPI + Vue 3 🚀 I Built an Ethical Hacking Scanner Tool – Open Source Project I Replaced /usage and /context in Claude Code With a Single Statusline A Pythonic Way to Handle Emails (IMAP/SMTP) with Auto-Discovery and AI-Ready Design I Collected 8.9 Million Polymarket Price Points — Here's What I Found About How Markets Really Move EcoTrack AI — Carbon Footprint Tracker & Dashboard Everyone's Using AI. No One Agrees How. 5 self-hosted ebook managers worth trying in 2026 Building Your First AI Agent with LangChain: From Chatbot to Autonomous Assistant Common SOC 2 Failures (Real World) Stop Vibe-Checking Your AI App: A Practical Guide to Evals How to Use SonarQube and SonarScanner Locally to Level Up Your Code Quality Your Next To-Do App Is Dead — I Replaced Mine with an OpenClaw AI Sign a Nostr event in 60 lines of Python using coincurve — no nostr-sdk, no nbxplorer, no rust toolchain ITGC Audit Explained Like You’re in Big 4 Patch Tuesday abril 2026: Microsoft parcha 163 vulnerabilidades y un zero-day en SharePoint Stop scraping everything: a better way to track competitor price changes Listing on MCPize + the Official MCP Registry while routing payments OUTSIDE the marketplace — how I kept 100% of my x402 revenue Building an AI-Powered Risk Intelligence System Using Serverless Architecture Why We Ripped Function Overloading Out of Our AI Toolchain Testing AI-Generated Code: How to Actually Know If It Works SaaS Churn Is Killing Your Business. Here Is What to Do About It (Without a Support Team) The Speed of AI Is No Longer Linear - And Self-Improving Models Are Why How to Implement RBAC for MCP Tools: A Practical Guide for Engineering Teams From Standard Quote to Persuasive Proposal: AI Automation for Arborists I built a CLI that scaffolds complete multi-tenant SaaS apps Axios CVE-2025–62718: The Silent SSRF Bug That Could Be Hiding in Your Node.js App Right Now The dashboard that ended our friendship Data Pipelines Explained Simply (and How to Build Them with Python) The Hidden Cost of AI Systems Nobody Talks About. undefined vs undeclared, and how typeof behaves Switching from file-based jobs to NATS/Kafka in Rust without changing code io_uring Adventures: Rust Servers That Love Syscalls Why Agentic AI is Killing the Traditional Database The POUR principles of web accessibility for developers and designers Quantum Neural Network 3D — A Deep Dive into Interactive WebGL Visualization How To Install Caveman In Codex On macOS And Windows Automation Pipeline Reliability: Why Your Workflow Breaks When Nobody Is Watching I Built an 'Open World' AI Coding Agent — It Works From ANY Folder From Freelancing to Product: A Tech Service Company's SaaS Transformation China's AI Giants: Adding Tencent Hunyuan & ByteDance Doubao to AI University (74 Providers) On the Vibe Coders and Their Lies clerk: Auto-Summarize Your Claude Code Sessions AI Weekly — 2026/04/10–04/17 | The Model Lockdown Is Here, but the Toolchain Is the Real Battleground AI 週報 — 2026/04/10–2026/04/17 模型封鎖潮來了,但工具鏈才是真戰場 Maybe this is how Open-Source apps are born... 🚀 Fine-Tune LLMs with LoRA and QLoRA: 2026 Guide tRPC v11 + Next.js App Router: End-to-End Type Safety Without the Boilerplate ShadCN UI in 2026: Why I Stopped Installing Component Libraries and Started Owning My Components SaaS Billing in React Server Components: Stripe + Supabase Without a Single `useEffect` Join our DEV Weekend Challenge — $1,000 in Prizes Across TEN winners! Submissions Due April 20 at 6:59 AM UTC. Implementing FSRS Spaced Repetition in Flutter + Supabase — Adding Memory Science to an AI Learning App "I Texted My Localhost From the Train — Claude Code Fixed the Bug Before I Got Home" I Built a Sales Prep AI and It Went Deeper Than Expected Design to Code #2: One JSON, Eleven Outputs Solving the 100M-Row Problem: A Summary Table Pattern for High-Volume Push Notification Logs Flutter Web With Wasm: What Actually Changes For Developers I Built 50 Royalty-Free Soundtracks for My Side Project in a Weekend Using AI Music Generation The Vibe Coding Security Checklist: 7 Things to Check Before You Ship Stop Letting Googlebot Guess Fix Your React App's SEO Right Desconstruindo o Streaming do LinkedIn: Como Criar um Engine de Extração de Vídeo de Alta Performance com HLS e FFmpeg (EDA Part-1) EDA (Exploratory Data Analysis) Explained With Real Life — Why Looking at Your Data Is the Most Important Step in Machine Learning Brand Relationship Management at Scale: Our 4-Touch Outreach System for 200+ Brands Why String.fromEnvironment() Might Return an Empty String in Dart JGuardrails 1.0.0 — Hardening Java LLM Apps Against Jailbreaks, Toxicity, and Prompt Injection Plan and Schedule a Full Week of Threads Content From One Claude Conversation Coding Cat Oran Ep3, Five Tables Changed Everything Updated: BFF Pattern I'm done watching freelancers get buried by 200 proposals. So I'm building the alternative. This is my first post BFS Algorithm in Java Step by Step Tutorial with Examples Tracking LLM Pricing Monthly: An Open Dataset for 22 AI Models How We Measure Content ROI on a Comparison Site: Revenue Attribution Without Perfect Data Introducing Nova AI Ops: The AI-Native Operating System for SRE Teams I built a free desktop video downloader for Windows — Grabbit How Talkie OCR Helps Vision-Impaired & Dyslexic Users Read the World Around Them VRCFaceTracking安装和iPhone面捕配置教程,有bug Even CrowdStrike Can't See Your Agents The Automation Gold Rush: What n8n Workflows and Claude Are Opening Up for Developers Right Now
dbtyper: TypeScript for SQL — a Compile-Time SQL Type Checker
Vadzim Zieńk · 2026-04-30 · via DEV Community

Write plain SQL. Get fully typed rows. No ORM. No query builder DSL. No N+1 footguns.


The SQL hole in your TypeScript codebase

TypeScript is great at catching errors early.

Rename a field, change a type, delete a property — the compiler tells you immediately, everywhere it matters. Refactoring is fast, feedback is instant, and the codebase stays coherent as it grows. LLMs make mistakes too, and that tight feedback loop lets them self-correct fast.

Then there's SQL.

SQL queries are plain strings.

You know the pain — you remember it from the pre-TypeScript days, when your functions were bloated with input parameter checks right at the top.

So now you rename a column in a table and instead of fixing red underlines in seconds, you run tests for minutes. If you have them.

The natural response is ORMs and query builders.

That adds layers of abstraction on top of abstractions built to hide other abstractions.

We've brought a dozen typed ORMs with different sets of supported SQL features (you'd never guess which) just to get that quick feedback from TypeScript — and instead we've cognitively overwhelmed ourselves and our LLMs, which still prefer to write plain SQL even in a NestJS project, because even soulless AI prefers readability and simplicity. Loop closed.

Let me say it again. LLMs already know SQL extremely well.

Every model has been trained on millions of SQL examples. When you ask an LLM to write SQL, it just writes SQL — correctly, concisely, in a form every developer on the team can read without translation. The price of this approach is unmaintainable code. If you're lucky you also have a ton of tests. And AI bills to pay, because all that code — the features, the tests, everything — is not free — it costs tokens and grabs context.

What we need is TypeScript for SQL.

Typed SQL string literals that know your schema and turn every query into a type-checked statement — so that schema changes propagate as type errors, instantly, without running anything.

That's dbtyper — a compile-time SQL parser written using the TypeScript type system.


Enter dbtyper

dbtyper is a TypeScript library that parses your SQL string literals at the type level and returns a properly typed array of rows — no codegen, no build step, no DSL to learn.

const rows = await db.query(`
    SELECT id, display_name as name, email FROM users
`)
// rows: Array<{ id: string; name: string; email: string }>

Enter fullscreen mode Exit fullscreen mode

const rows = await db.query(`SELECT * FROM users`)
// rows: Array<{ id: string; email: string; display_name: string; login_count: number... }>

Enter fullscreen mode Exit fullscreen mode

The types come from your schema, which is declared once through your migration chain. The SQL string is the source of truth for which columns appear in the result.


Setup

1. Define your database through migrations

// db/index.ts
import { sqlMigrations } from "dbtyper"
import type { PostgresDriver } from "dbtyper/postgres"

export async function exampleDb(driver: PostgresDriver) {
    return sqlMigrations({ driver })
        .apply((await import("../migrations/001.do.schemas.js")).generateSql())
        .apply((await import("../migrations/002.do.users.js")).generateSql())
        .apply((await import("../migrations/003.do.agenda.js")).generateSql())
        .apply((await import("../migrations/004.do.seed_users.js")).generateSql())
        .database()
}

Enter fullscreen mode Exit fullscreen mode

Each migration contributes to the accumulated type-level schema. By the end of the chain, the library knows the shape of every table.

2. Connect and query

import postgres from "postgres"
import { postgresSqlDriver } from "dbtyper/postgres"
import { exampleDb } from "./db/index.js"

const db = await exampleDb(postgresSqlDriver({ sql: postgres(connectionString, { max: 10 }) }))

const rows = await db.query(`
    SELECT
        public.agenda.*,
        email,
        display_name,
        auth.users.created_at,
        auth.users.login_count
    FROM auth.users
    LEFT JOIN public.agenda
        ON auth.users.id = public.agenda.user_id
    ORDER BY email
`)

Enter fullscreen mode Exit fullscreen mode


What the IDE sees

Simple SELECT with named columns

const rows = await db.query(`SELECT id, name FROM users`)

Enter fullscreen mode Exit fullscreen mode

IDE hover showing inferred rows type for selected columns

Hover over rows in your IDE and the inferred type is available immediately:

const rows: Array<{
    id: string
    name: string
}>

Enter fullscreen mode Exit fullscreen mode

The type tooltip appears inline — no annotation, no manual generic.


SELECT * expands to the full table shape

const rows = await db.query(`SELECT * FROM users`)

Enter fullscreen mode Exit fullscreen mode

IDE hover showing SELECT star expanded to the full table shape

Hover over rows and SELECT * expands to the known table shape:

const rows: Array<{
    id: string
    name: string
    email: string
    phone: string
    created_at: Date
    login_count: number
}>

Enter fullscreen mode Exit fullscreen mode


JOIN across schemas — types merge correctly

const rows = await db.query(`
    SELECT
        public.agenda.*,
        email,
        display_name,
        auth.users.created_at,
        auth.users.login_count
    FROM auth.users
    LEFT JOIN public.agenda ON auth.users.id = public.agenda.user_id
    ORDER BY email
`)

Enter fullscreen mode Exit fullscreen mode

IDE hover showing joined query result type across schemas

Hover over rows and the joined result type is merged from both tables:

const row: {
    agenda: string
    created_at: Date
    display_name: string
    email: string
    id: string
    login_count: number
    title: string
    user_id: string
}

Enter fullscreen mode Exit fullscreen mode


Schema change = instant compile error

Rename a column in your migration (phonephone_number), and every query() call that references phone becomes a type error — immediately, without running anything.

IDE error showing a missing property after a schema change

After a column rename, TypeScript reports the stale property access:

Property 'phone' does not exist on type '{ id: string; name: string; phone_number: string; ... }'

Enter fullscreen mode Exit fullscreen mode


Params are supported

const rows = await db.query(`SELECT * FROM users WHERE id = :id AND active = :active`, { id: 42, active: true })

Enter fullscreen mode Exit fullscreen mode


The full API

export type DataBase<Db> = {
    // Typed query — statement parsed at type level
    query<Stmt extends string>(statement: Stmt): Promise<Array<SqlSelectRowObject<Db, Stmt>>>
    query<Stmt extends string, Params extends ExpressionParamsShape>(
        statement: Stmt,
        params: ParamRuntimeValues<Params>,
    ): Promise<Array<SqlSelectRowObject<Db, Stmt, Params>>>

    // Escape hatch for unsupported features or gradual migration
    queryUntyped(statement: string, params?: Record<string, unknown>): Promise<Array<any>>

    // Streaming — same type inference, async iterable
    stream<Stmt extends string>(statement: Stmt): AsyncIterable<SqlSelectRowObject<Db, Stmt>>
    stream<Stmt extends string, Params extends ExpressionParamsShape>(
        statement: Stmt,
        params: ParamRuntimeValues<Params>,
    ): AsyncIterable<SqlSelectRowObject<Db, Stmt, Params>>

    streamUntyped(statement: string, params?: Record<string, unknown>): AsyncIterable<any>
}

Enter fullscreen mode Exit fullscreen mode

queryUntyped / streamUntyped exist for two reasons: gradual codebase migration, and SQL features the parser doesn't support yet (more on that below).


Current limitations (and why they don't block you)

dbtyper is under active development. The type-level SQL parser currently handles:

SELECT col1, col2 as col3 — named columns and aliases
SELECT * or SELECT table.* — full table expansion

LEFT JOIN, INNER JOIN, cross-schema joins

✅ SQL parameters — validated at compile time

ORDER BY, LIMIT, OFFSET

✅ Multi-schema databases (auth.users, public.sales)

✅ Streaming results

✅ Computed expressions with operators (price * quantity AS total, a + b AS sum)

✅ Accessing a column that doesn't exist in the query result — compile-time error

🚧 Not yet supported:

  • GROUP BY / HAVING
  • Built-in SQL functions — partial support only
  • User-defined functions
  • User-defined types — enums are surfaced as string
  • Nested subqueries
  • Better compile-time diagnostics when the SQL string itself is malformed — planned

For anything in the 🚧 list, queryUntyped is your bridge. You don't have to delay adopting the library — use typed queries where you can, untyped where you can't, and migrate as the parser grows.


Why this matters more than "nicer types"

1. The abstraction stack collapses

With a type-safe raw SQL primitive, you don't need the query builder that generates it, the ORM that wraps the builder, or the repository that wraps the ORM. One layer. One concept.

// Instead of this (4 layers, N+1 risk):
const users = await userRepository.findAll({ include: ["orders"] })

// Just write the JOIN:
const rows = await db.query(`
    SELECT u.id, u.name, o.total
    FROM users u
    JOIN orders o ON u.id = o.user_id
`)
// rows: Array<{ id: string; name: string; total: number }>

Enter fullscreen mode Exit fullscreen mode

2. Schema changes propagate instantly

The DB boundary is normally a black hole for TypeScript's type system. Migrations run, columns change, and your code only finds out at runtime. With dbtyper, the schema lives in the type system. A migration is a type-level event.

3. LLMs write better code with this

SQL is universal. Every model has seen way more SQL examples than ORM ones.

With dbtyper:

  • The model writes plain SQL — shorter, clearer, more reliable
  • The type checker catches errors in the same edit cycle — no DB roundtrip needed
  • Schema changes are immediately visible as type errors — the model can fix them without running anything

The feedback loop collapses from write → run tests or deploy & run → crash → reason to write → type error → fix.


Interested? More:

Is it production ready?

Not at all. It's still alpha.


SQL already was the right abstraction. It just needed types.

Thanks for reading — btw I'm looking for a long-term project, if you know one — let me know.