Free checker
Paste package.json, route handlers, server components, server actions or lib files. Separate files with a line like // app/api/route.ts to get per-file results.
Before β after (what --fix does)
Before Β· @vercel/kv + @vercel/postgres
import { kv } from '@vercel/kv'import { sql } from '@vercel/postgres' export async function GET() { const hits = await kv.incr('hits') const { rows } = await sql<User>`SELECT * FROM users` return Response.json({ hits, rows })}
After Β· @upstash/redis + Neon
import { sql } from '@/lib/db/neon-compat'import { Redis } from '@upstash/redis'const kv = Redis.fromEnv() // reads KV_REST_API_*export async function GET() { const hits = await kv.incr('hits') const { rows } = await sql<User>`SELECT * FROM users` return Response.json({ hits, rows })}
Your { rows } destructuring and sql<User> generics keep working: the pack writes lib/db/neon-compat.ts, a 30-line wrapper on neon(url, { fullResults: true }) that reads the same POSTGRES_URL as before (then DATABASE_URL). Redis.fromEnv() reads your existing KV_REST_API_URL/TOKEN, so no env renames.
Migration map
| @vercel/kv Β· @vercel/postgres | Upstash Β· Neon | Pack |
|---|---|---|
import { kv } from '@vercel/kv' | Redis.fromEnv() from @upstash/redis | β auto |
createClient({ url, token }) (KV) | new Redis({ url, token }) | β auto |
kv.scanIterator() & h/s/z variants | scanIterator(kv, opts) helper (generated) | template + flag |
sql`β¦`, sql<T>`β¦`, sql.query() | same calls via neon(url, { fullResults: true }) shim | β auto |
Pool, Client, QueryResult, types β¦ | same names from @neondatabase/serverless | β auto |
db.connect() / sql.connect() | new Pool() per request (+ ws on Node β€21) | flag + recipe |
createPool(), postgresConnectionString() | new Pool({ connectionString }), process.env.POSTGRES_URL | flag + recipe |
sql('β¦ $1', [v]) plain call | sql.query('β¦ $1', [v]) (Neon 1.x throws) | flag |
package.json deps + engines.node < 19 | @upstash/redis, @neondatabase/serverless, Node 20+ | β deps / flag engines |
The 4 ways a hand migration breaks production
{ rows } is undefined
neon() returns an array, not a QueryResult. A blind search-and-replace turns every const { rows } = await sql`β¦` into undefined at runtime, with no type error if you used any.scanIterator is gone
@vercel/kv added scanIterator/hscanIterator/β¦; @upstash/redis doesn't have them. Cache-busting and session cleanup jobs crash.connect() doesn't exist
Transactions via db.connect() need a Neon Pool created and closed per request (plus ws on Node β€21).Node 18 builds fail
@neondatabase/serverless 1.x needs Node 19+, and sql('β¦') plain calls now throw. The checker flags your engines and every call.How it works
node cli.js . --md report.md lists every finding with file:line and the fix.--dry-run prints the unified diff. Nothing is written.--fix rewrites safe patterns (.bak backups), writes the shim + scan helper, updates package.json.--ci exits 1 until nothing breaking is left. Put it in your pipeline.Vercel KV & Postgres β Upstash + Neon Migration Pack
- Codemod CLI (
node cli.js . --fix): rewrites@vercel/kvimports,require()andcreateClientto@upstash/redis; movessql/dbto a Neon shim and Pool/Client/type re-exports to@neondatabase/serverless; updates package.json. Only exact, known-safe patterns; .bak backups;--dry-rundiff. - Generated helpers (TS + JS):
lib/db/neon-compat(keeps{ rows, rowCount, fields }+sql<T>) andlib/kv-scan(scan/hscan/sscan/zscan iterators). TypeScript passestsc --strictagainst the real packages. - CI gate + reports:
--ci,--json,--mdchecklist report. - 17-step migration checklist (env vars, Pool transactions, preview branches, ORMs) + 34 regression tests + fixtures; every rule cites npm, Neon, Upstash or Vercel.
Instant download: after checkout you're redirected straight to your private download page. Zero dependencies, runs locally on Node 18+.
Get the 10-Second Fix Bundle β $9Cheaper than one hour of debugging a broken { rows }. Questions or download problems? GitHub issues.
Methodology
The free checker runs entirely in your browser: it matches the text you paste against rules written from the official changelogs and migration guides. Nothing is uploaded. The paid deliverables are files (codemod, templates, checklist) unlocked right after checkout; each pack is tested against the fixtures shipped inside it.
Sources and free assets: GitHub release Β· GitHub Wiki guide. Contact and support: open a Discussion at patchvault-81y.pages.dev.
Last Updated:
FAQ
Is @vercel/kv deprecated?
Yes. @vercel/kv 3.0.0 is deprecated on npm with the message "Vercel KV is deprecated. If you had an existing KV store, it should have moved to Upstash Redis". Vercel moved existing KV stores to Upstash Redis in December 2024. The replacement SDK is @upstash/redis; @vercel/kv was itself a thin subclass of its Redis client.
Is @vercel/postgres deprecated?
Yes. @vercel/postgres 0.10.0 is deprecated on npm and Vercel Postgres databases were moved to Neon as a native Vercel integration in December 2024. Neon's official guide replaces it with @neondatabase/serverless (the neon() HTTP driver or its Pool/Client).
Do I need to move my data?
No. Vercel already moved existing stores: your KV data is in an Upstash Redis database and your Postgres data is in a Neon database, both linked to your Vercel project. You only need to migrate your code off the deprecated SDKs.
Do I have to rename KV_REST_API_URL and KV_REST_API_TOKEN?
No. Redis.fromEnv() in @upstash/redis reads UPSTASH_REDIS_REST_URL and UPSTASH_REDIS_REST_TOKEN and falls back to KV_REST_API_URL and KV_REST_API_TOKEN, so `const kv = Redis.fromEnv()` works with your existing env vars.
Why does const { rows } = await sql`...` break after switching to neon()?
neon() returns just the array of rows by default, while @vercel/postgres returned a node-postgres QueryResult with rows, rowCount and fields. Either change the call sites to `const rows = await sql`...``, or create the client with neon(url, { fullResults: true }). The pack's generated lib/db/neon-compat shim uses fullResults so your existing code and sql<T> generics keep working.
What replaces sql.connect() and db.connect()?
The neon() HTTP driver has no connect(). For interactive transactions or sessions use Pool or Client from @neondatabase/serverless (WebSockets; on Node 21 and earlier set neonConfig.webSocketConstructor = ws), created and closed inside the request. For non-interactive batches, sql.transaction([...]) works over HTTP.
What replaces kv.scanIterator()?
@upstash/redis has scan, hscan, sscan and zscan but not the *Iterator helpers that @vercel/kv added. The pack writes a lib/kv-scan helper with the same async-iterator API and the checker flags every call.
Is my code uploaded anywhere?
No. This free checker runs entirely in your browser. The paid pack is a zero-dependency Node.js CLI that runs locally on your repo.
How do I get the pack after paying?
Instantly: after checkout Stripe redirects you to your private download page (the unlock key is in the link). Save the link; it is your copy.
More migration packs
Supabase auth-helpers β @supabase/ssr
Free checker + codemod for createClientComponentClient, createServerComponentClient, middleware β proxy.Shopify CLI 5.0 checker
Find what breaks in your theme/app workflow before upgrading Shopify CLI.Free starter scanners: all PatchVault tools