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

推荐订阅源

CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
L
Lohrmann on Cybersecurity
aimingoo的专栏
aimingoo的专栏
V
V2EX
S
Security Affairs
T
Threatpost
C
CXSECURITY Database RSS Feed - CXSecurity.com
IT之家
IT之家
J
Java Code Geeks
The Register - Security
The Register - Security
U
Unit 42
C
CERT Recently Published Vulnerability Notes
月光博客
月光博客
A
About on SuperTechFans
H
Hackread – Cybersecurity News, Data Breaches, AI and More
T
The Blog of Author Tim Ferriss
Cisco Talos Blog
Cisco Talos Blog
Project Zero
Project Zero
S
Schneier on Security
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
D
DataBreaches.Net
博客园 - 司徒正美
V
Vulnerabilities – Threatpost
T
Tor Project blog
Security Latest
Security Latest
T
The Exploit Database - CXSecurity.com
T
Threat Research - Cisco Blogs
Scott Helme
Scott Helme
Application and Cybersecurity Blog
Application and Cybersecurity Blog
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
M
MIT News - Artificial intelligence
云风的 BLOG
云风的 BLOG
小众软件
小众软件
L
LangChain Blog
Attack and Defense Labs
Attack and Defense Labs
Recent Commits to openclaw:main
Recent Commits to openclaw:main
P
Palo Alto Networks Blog
A
Arctic Wolf
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
C
Cyber Attacks, Cyber Crime and Cyber Security
博客园 - 叶小钗
D
Darknet – Hacking Tools, Hacker News & Cyber Security
L
LINUX DO - 最新话题
MongoDB | Blog
MongoDB | Blog
Webroot Blog
Webroot Blog
H
Hacker News: Front Page
Know Your Adversary
Know Your Adversary
Spread Privacy
Spread Privacy
AWS News Blog
AWS News Blog
Engineering at Meta
Engineering at Meta

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
One query language for SQL, Mongo, and the browser: the case for Forge
Johnson Fash · 2026-06-29 · via DEV Community

There's a moment in most TypeScript backend projects when the data layer stops being one thing.

Maybe you started on Postgres, then a feature shipped where audit events made more sense as a flat Mongo document. Maybe your mobile app needs to work on the train, so you added SQLite in the browser. Maybe a "find me the three nearest locations" endpoint forced you to drop into raw ST_Distance_Sphere() because your ORM didn't know what a geo point was. Maybe you added embeddings for an AI search box and now half your query path is a $queryRaw with a hand-rolled vector cosine clause.

Each of those moments seems small in isolation. Stacked, they're how a typed data layer becomes four data layers — a Prisma client over here, a Mongoose schema over there, Dexie wrapping IndexedDB, and a folder full of raw SQL strings nobody wants to touch. Each layer has its own concept of "model," its own migration story, its own way of describing a where clause. The code in your services is suddenly half plumbing.

I built Forge because every other TypeScript ORM I tried forced me to pick one surface and treat the rest as a foreign country.


(https://www.npmjs.com/package/forge-orm)

The constraint that produced it

I was working on a multi-tenant SaaS that had to:

  • Run Postgres for transactional state
  • Use Mongo for high-volume nested documents that didn't want a schema
  • Work fully offline in the browser on a Tauri-packaged client, including a point-of-sale flow that took payments without a network
  • Geocode every location and answer nearest-neighbor queries on them
  • Eventually add embeddings so an operator could type "blue cotton tee" and find products without exact keyword matches

Pick any two of those and the existing ORMs work. Pick three and you start writing glue. Pick all five and you spend more time keeping four data layers in sync than building features.

Prisma covers Postgres beautifully and treats Mongo as a second-class adapter. It has no browser story at all. Geo is raw SQL. Vector is raw SQL.

Drizzle is the cleanest TS-native SQL builder I've ever used — but it's SQL-only. No Mongo. No browser. Geo and vector escape into hand-written fragments.

Kysely has the best types of any query builder, but it's also SQL-only and intentionally not an ORM. You write joins. No relations sugar. No browser.

TypeORM has Mongo support, but the types degrade to any so often the TypeScript story is barely real, and decorators don't compose with edge runtimes.

Mongoose handles Mongo well. It does not handle Postgres at all.

Dexie handles IndexedDB. It does not handle anything else.

So you stack them. And you write a small in-house abstraction layer on top so service code doesn't have to know which one it's hitting. And that layer eats a month every year just keeping up.

Forge is what happens when you decide that's not acceptable and try to put one query language under all of it.


Video: https://youtu.be/FpOaneoCzpw

What you actually get

*One vocabulary across six dialects
*

Forge has adapters for Postgres, MySQL, SQLite, Mongo, DuckDB, and MSSQL. The same query method names work on all six:

// Postgres
await db.User.findMany({ where: { tier: "pro" }, limit: 20 })

// Mongo
await db.User.findMany({ where: { tier: "pro" }, limit: 20 })

// DuckDB (analytical)
await db.User.findMany({ where: { tier: "pro" }, limit: 20 })

That's not "compatible-ish." Those are byte-identical service-layer calls. The adapter underneath translates where, limit, orderBy, select, include, groupBy, upsert, transaction, and the rest into the dialect's native form. A Mongo $or becomes a SQL OR. A SQL NOT IN becomes Mongo's $nin. JSON path access becomes ->> on Postgres and dotted-key access on Mongo.

The point isn't that you'll switch databases (you mostly won't). The point is that when a feature lands that needs the other database, you don't relearn an ORM. You point a different db at the same schema and write the same code.

The browser is a first-class target

This is the part I haven't seen elsewhere.

Forge ships a browser adapter built on sqlite-wasm + OPFS, running in a Web Worker. It mounts as opfs:/your-db.sqlite or opfs-sahpool:/... for synchronous-access-handle mode, or :memory: for tests. There are Vite, Next.js, and Webpack plugins that handle the wasm loading without you wiring it.

// In the browser. Same API as the server.
const db = await createBrowserDb({ schema, url: "opfs:/dallio.sqlite" })
await db.Invoice.create({ data: { number: "INV-3308", total: 4250 } })
const drafts = await db.Invoice.findMany({ where: { status: "draft" } })

The query you wrote for your server's Postgres works in the user's browser against SQLite. You stop maintaining two data layers because the same model is the same model. Offline drafts, offline POS sales, offline product catalogs — all queryable with the same findMany your service code uses, no IndexedDB wrapper, no Dexie schema duplicated from your Postgres schema.

For a Tauri app or a PWA that needs to keep working when the network drops, this collapses an entire architectural sub-project into a config flag.

Geo, vector, and JSON path are typed


const Place = f.model("places", {
  id: f.id(),
  name: f.string(),
  location: f.geoPoint({ srid: 4326 }),
  embedding: f.vector(1536),
  details: f.json<{ openingHours: string[]; tags: string[] }>(),
})

// Nearest 10 places to a point — Postgres uses PostGIS, MySQL uses
// ST_Distance_Sphere, Mongo uses $near. Same query call.
const nearby = await db.Place.findMany({
  where: { location: { nearTo: { point: [3.42, 6.45], withinMeters: 5000 } } },
  limit: 10,
})

// Vector similarity — same vocabulary as geo
const similar = await db.Place.findMany({
  where: { embedding: { nearTo: { vector: queryEmbedding, take: 5 } } },
})

// Typed JSON path — no string fragments
const cafes = await db.Place.findMany({
  where: { "details.tags": { contains: "cafe" } },
})

None of those require raw SQL. None require a separate vector library. None require a PostGIS adapter package. The results are typed — nearby[0].location is a [number, number], similar[0].embedding is number[], details.tags is string[].

If you've ever written a cosine_similarity() SQL fragment by hand or tried to thread PostGIS through Prisma raw queries, you know what the absence of this costs.

No binary engine, no codegen step

Forge is pure TypeScript. There is no Rust query engine that needs to be downloaded per platform, no prisma generate step in your CI, no .prisma schema file written in a custom DSL.

Your schema is a TS file. Models are values. Importing them gives you typed query methods. That's the whole setup.

Cold start on Lambda or Vercel Functions is under 50ms, the same as Drizzle and Kysely, an order of magnitude faster than Prisma's historical baseline. Edge runtimes (Workers, Vercel Edge, Bun, Deno) work natively because nothing about Forge requires Node-specific APIs.

Drift detection that works in production

const report = await db.$migrate()
// {
//   alteredColumns: [{ table: "users", column: "phone", action: "added" }],
//   pending: [{ table: "users", column: "legacy_id", action: "drop" }],
// }

db.$migrate() walks the live schema against the model definitions. Safe drift (an added column, a new index) gets applied automatically. Destructive drift (a column that no longer exists in the model) is surfaced under pending for your migration tool to handle deliberately. You can opt out with db.$migrate({ alter: false }).

db.$diff(driver, { schema }) returns the same report without applying anything. db.$doctor() runs a live probe — checks extensions, indexes, encoding, latency — and tells you what's wrong. Both work in the browser too, against the user's OPFS SQLite.

In practice this means the day a new feature ships with two new columns, you don't need to ship a migration file. You can let $migrate() apply the additions on boot and ship a migration for the destructive part next sprint. Or, if you're more conservative, you can run $diff() in CI and refuse to deploy when drift is detected. Both flows work.

Atomic upsert that actually is atomic

await scopedDb.User.upsert({
  where: { email },
  create: { email, name, tier: "free" },
  update: { lastSeenAt: now },
})

That call compiles to INSERT ... ON CONFLICT DO UPDATE on Postgres, INSERT ... ON DUPLICATE KEY UPDATE on MySQL, MERGE on MSSQL,

findOneAndUpdate({ upsert: true }) on Mongo, and the SQLite equivalent. One round trip. No find-then-branch race condition. No accidental double-insert under concurrency.

I mention this because it's not glamorous, but every ORM that doesn't do it correctly produces a class of intermittent production bugs you can never reproduce locally and your tracing tool blames on "user double-clicked." Forge does it correctly across all six dialects.


What Forge is not

I'll be direct about this because honest tradeoffs are part of why anyone trusts a recommendation.

There is no Studio. Prisma Studio is a category-defining piece of tooling. db.$doctor is CLI-only and doesn't browse data. If your team includes people who want to click around tables in a GUI, Forge will frustrate them until a Studio ships.

There is no SaaS company behind it. That has good and bad sides. No paid Accelerate/Pulse/Optimize tier means the OSS roadmap isn't shaped by upsell pressure. No SaaS company also means no support contract, no enterprise SLA, no Salesforce conference booth, no team of dedicated maintainers if I get hit by a bus.

The community is tiny. If you Google a Forge error, you'll mostly find the README. Stack Overflow won't help. There are no YouTube tutorials. Onboarding a junior engineer takes longer because the answer to "how do I do X" is "read the source," not "watch this 20-minute video."

Depth versus breadth. Drizzle has gone deeper into Postgres-specific edge cases than Forge has. Mongoose has gone deeper into Mongo's aggregation pipeline. Forge covers six dialects well, but a dialect-specific ORM will always go deeper into its one dialect. If you need Postgres logical replication consumer slots or Mongo's exact change-stream resume semantics surfaced as a first-class API, you'll write that integration yourself in Forge.

It's young. First release was 2024. 2.5.3 is the current version. The shape is stable, the test suite is large (the test file count crossed 470 in 2.5.3, mostly per-dialect parity), but five-year battle scars accumulate things you can't get faster. Expect a few rough edges.

If those are dealbreakers, Drizzle or Prisma is your answer. That's a real choice, not a consolation prize.


When Forge is the right answer

Pick Forge when:

  • Your data already lives in more than one place. Postgres + Mongo. Postgres + SQLite (mobile sync). DuckDB for analytics over a transactional Postgres. MSSQL for a legacy system plus Postgres for the new one. Forge collapses the abstraction tax of running multiple ORMs.
  • You're building an offline-first or local-first app. Tauri desktop apps, Capacitor mobile apps, PWAs that need to work on flaky connections. Forge's browser adapter means you maintain one data layer instead of one server data layer plus one client IndexedDB layer plus the sync code between them.
  • Geo or vector is on your roadmap. Even if you don't need them today, building them on raw SQL fragments is a permanent tax. Forge surfaces both as typed primitives.
  • You care about cold starts. Lambda, Vercel Functions, Cloudflare Workers — Forge is pure TS, no binary, no codegen runtime. Drizzle and Kysely match it; Prisma still doesn't.
  • You like writing code more than configuration. The schema is a TS file. The migration story is "the live schema and the models disagree, here's what changed." There's no DSL, no codegen step, no separate migration CLI to learn.

Don't pick Forge when:

  • You have a senior Postgres-only team that already loves Drizzle. Forge wouldn't make their day better.
  • You need Studio and your stakeholders aren't going to budge.
  • You're shipping inside a company that requires a vendor with a support contract.

Getting started

npm install forge-orm@^2.5.3

Define a model file:

// schema.ts
import { f } from "forge-orm"

export const User = f.model("users", {
  id: f.id(),
  email: f.string({ unique: true }),
  name: f.string(),
  tier: f.enum(["free", "pro", "enterprise"]).default("free"),
  createdAt: f.timestamp().defaultNow(),
})

export const schema = { User }

Connect from your server:

import { createPostgresDb } from "forge-orm/postgres"
import { schema } from "./schema"

export const db = createPostgresDb({ schema, url: process.env.DATABASE_URL! })
await db.$migrate() // applies safe drift on boot

Or from the browser:

import { createBrowserDb } from "forge-orm/browser"
import { schema } from "./schema"

export const db = await createBrowserDb({ schema, url: "opfs:/app.sqlite" })
await db.$migrate()

The same db.User.findMany({ where: { tier: "pro" } }) works in both. That is the entire setup.


A statement about TypeScript data layers in 2026

The data layer should follow your data, not the other way around. When a feature pushes you toward a different database — analytical, document, offline — the ORM should come with you, not force you to maintain a second one.

Most TypeScript ORMs were built when "the database" meant Postgres or maybe MySQL. They optimize for the depth of that single relationship. That's an honest tradeoff and it produces excellent tools for the case where it's correct.

But TypeScript apps in 2026 aren't single-database anymore. They run partially in the browser. They store transactional data in one engine and analytical data in another. They pull embeddings out of vector indexes and locations out of geo indexes. They sync some state offline and reconcile it when the network returns.

Forge is what an ORM looks like if you take that reality as the starting point.

It is not the safe choice. The safe choice is Prisma. The cool choice is Drizzle. The purist choice is Kysely.

Forge is the choice when none of those covers your data's actual shape, and you'd rather pay the cost of a younger tool than the cost of running four mature ones in parallel for the rest of the project's life.

If that's where you are, forge-orm@^2.5.3 (https://www.npmjs.com/package/forge-orm) is one npm install away.