Schlez
2023
Running Vercel Postgres Locally

Running Vercel Postgres Locally

The setup I use for local work with Vercel Postgres or Neon database.Published .

Ship week was huge. Vercel announced new storage offerings. Alongside KV, Blob, and the production-ready Edge Config, we announced a full PostgreSQL offering based on Neon.

I am very excited that we have this offering. First, because I think Postgres databases are amazing. SQL is a champion for so many things. Second, Neon worked hard to make their database (and therefore Vercel Postgres) work on Edge Functions, which means lower cold boots and cheaper compute—and I’m excited.

Previously, my side projects were using Supabase. This was because they were Postgres with an HTTP API if I wanted to have an Edge Function. This caused me to use 2 different querying solutions. One was Prisma, and the other was the Supabase TypeScript library. Not anymore. Now I can use Kysely and query my database with the same constructs. It’s much simpler and works amazingly well.

One question still remains though: how can I use Vercel Postgres while running my local apps? I am a strong believer in running the infra locally when developing. This is probably because my internet doesn’t work that good and most services are deployed on the US. I am not going to wait for packets to be sent to Virginia from my apartment near Tel Aviv.

Under the hood, the Vercel Postgres connector is using a WebSocket connection. This is a very smart decision from the Neon team, using HTTP enables running almost everywhere. But PostgreSQL doesn’t speak WebSocket natively, so how can we host it ourselves?

I asked George MacKerron (aka @jawj) about it and he linked me their neondatabase/wsproxy repo and let me tell ya, it’s working very well. To make it work, though, you’ll need to configure the client for local development.

To run the database and proxy locally, you can use this docker-compose.yaml file, which runs a PostgreSQL instance on port 5432, and a Neon WebSocket proxy on port 5433:

services:
  postgres:
    image: "postgres:15.2-alpine"
    environment:
      POSTGRES_USER: postgres
      POSTGRES_PASSWORD: postgres
      POSTGRES_DB: postgres
    ports:
      - "5432:5432"
  pg_proxy:
    image: ghcr.io/neondatabase/wsproxy:latest
    environment:
      APPEND_PORT: "postgres:5432"
      ALLOW_ADDR_REGEX: ".*"
      LOG_TRAFFIC: "true"
    ports:
      - "5433:80"
    depends_on:
      - postgres

When connecting to the client, you’ll need to use Neon’s adapters (and not Vercel’s for now) and configure them like so:

import { NeonDialect } from "kysely-neon";
import { Kysely } from "kysely";
import type { DB } from "@/generated/db/types";
import { neonConfig } from "@neondatabase/serverless";

// if we're running locally
if (!process.env.VERCEL_ENV) {
  // Set the WebSocket proxy to work with the local instance
  neonConfig.wsProxy = (host) => `${host}:5433/v1`;
  // Disable all authentication and encryption
  neonConfig.useSecureWebSocket = false;
  neonConfig.pipelineTLS = false;
  neonConfig.pipelineConnect = false;
}

if (!process.env.POSTGRES_URL) {
  throw new Error("POSTGRES_URL is not set");
}

export const db = new Kysely<DB>({
  dialect: new NeonDialect({
    connectionString: process.env.POSTGRES_URL,
  }),
});

And now you can query and mutate data on your Vercel Postgres both on production, and with a local Postgres database when working locally.

As time goes on, I believe this setup will become easier or even not existent. I will update this blog post once we have a better solution. But for now, it’s nice to know that it is possible and works well.

Read more