Skip to content
Discord Get Started

Remix

Remix connects to DB9 through any standard PostgreSQL driver over pgwire. Loaders and actions run server-side, so your database credentials stay off the client. No special adapter or driver is needed.

  • A DB9 database (create one)
  • Node.js 18+
  • Remix 2+
Terminal
db9 create --name remix-app

Get the connection string:

Terminal
db9 db status remix-app

Set the connection string as an environment variable:

.env
DATABASE_URL="postgresql://remix-app.admin:YOUR_PASSWORD@pg.db9.io:5433/postgres?sslmode=require"
Terminal
npx create-remix@latest remix-db9
cd remix-db9
npm install prisma @prisma/client
npx prisma init

Define your schema:

prisma/schema.prisma
generator client {
provider = "prisma-client-js"
}
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}
model User {
id Int @id @default(autoincrement())
email String @unique
name String
posts Post[]
createdAt DateTime @default(now())
}
model Post {
id Int @id @default(autoincrement())
title String
content String?
published Boolean @default(false)
author User @relation(fields: [authorId], references: [id])
authorId Int
createdAt DateTime @default(now())
}

Push the schema to DB9:

Terminal
npx prisma db push
npx prisma generate

Create a singleton client to avoid connection exhaustion during development:

app/db.server.ts
import { PrismaClient } from '@prisma/client';
let prisma: PrismaClient;
declare global {
var __prisma: PrismaClient | undefined;
}
if (process.env.NODE_ENV === 'production') {
prisma = new PrismaClient();
} else {
if (!global.__prisma) {
global.__prisma = new PrismaClient();
}
prisma = global.__prisma;
}
export { prisma };

Loaders run on the server before a route renders. Use them to fetch data from DB9.

app/routes/users.tsx
import type { LoaderFunctionArgs } from '@remix-run/node';
import { json } from '@remix-run/node';
import { useLoaderData } from '@remix-run/react';
import { prisma } from '~/db.server';
export async function loader({ request }: LoaderFunctionArgs) {
const users = await prisma.user.findMany({
include: { posts: true },
orderBy: { createdAt: 'desc' },
});
return json({ users });
}
export default function UsersPage() {
const { users } = useLoaderData<typeof loader>();
return (
<ul>
{users.map((user) => (
<li key={user.id}>
{user.name} — {user.posts.length} posts
</li>
))}
</ul>
);
}

Actions handle form submissions and other mutations, also server-side.

app/routes/users.new.tsx
import type { ActionFunctionArgs } from '@remix-run/node';
import { json, redirect } from '@remix-run/node';
import { Form } from '@remix-run/react';
import { prisma } from '~/db.server';
export async function action({ request }: ActionFunctionArgs) {
const formData = await request.formData();
const email = formData.get('email') as string;
const name = formData.get('name') as string;
await prisma.user.create({ data: { email, name } });
return redirect('/users');
}
export default function NewUserPage() {
return (
<Form method="post">
<input type="text" name="name" placeholder="Name" required />
<input type="email" name="email" placeholder="Email" required />
<button type="submit">Create User</button>
</Form>
);
}
  • Server-side only: Loaders and actions run on the server. Never import your db.server.ts module from a client-side file.
  • Port 5433: DB9 uses port 5433, not the default PostgreSQL port 5432.
  • TLS required: Always include sslmode=require in the connection string for DB9’s hosted service.
  • Node.js runtime required: DB9 uses the pgwire protocol over TCP. Deploy Remix on a Node.js server (Express, Hono, etc.), not on an edge runtime without TCP socket support.
  • Connection pooling: Start with 5–10 connections. The singleton pattern in db.server.ts prevents pool exhaustion during development HMR cycles.

DB9 uses port 5433, not 5432. Check that your DATABASE_URL includes :5433 in the connection string.

DB9 requires a TCP connection (pgwire protocol). If you deploy Remix to an edge runtime (e.g., Cloudflare Workers), database queries will fail. Use a Node.js-based server adapter instead.

Remix’s dev server reloads modules on file changes. Without the singleton pattern in db.server.ts, each reload opens a new connection pool. If you see “too many connections” errors, verify you are using the global caching pattern shown above and restart the dev server.

DB9 has limited information_schema support. If schema push fails, create tables with raw SQL instead. See the Prisma guide for details.