Remix JS is a powerful full-stack web framework that excels in handling server-side operations, including database interactions. In this guide, we'll explore how to work with databases in Remix, covering everything from setup to advanced techniques.
Remix is database-agnostic, meaning you can use any database system that works with Node.js. Popular options include:
Your choice depends on your project requirements, scalability needs, and familiarity with the technology.
To connect to a database in Remix, you'll typically use an ORM (Object-Relational Mapping) tool or a database driver. Let's look at an example using Prisma, a popular ORM:
npm install prisma @prisma/client
npx prisma init
prisma/schema.prisma
file:datasource db { provider = "postgresql" url = env("DATABASE_URL") }
db.server.ts
file in your app
directory:import { PrismaClient } from "@prisma/client"; let db: PrismaClient; declare global { var __db: PrismaClient | undefined; } if (process.env.NODE_ENV === "production") { db = new PrismaClient(); } else { if (!global.__db) { global.__db = new PrismaClient(); } db = global.__db; } export { db };
This setup ensures that you have a single database connection instance across your application.
With Prisma, you define your data models in the schema.prisma
file. Here's an example:
model User { id Int @id @default(autoincrement()) email String @unique name String? posts Post[] createdAt DateTime @default(now()) updatedAt DateTime @updatedAt } 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()) updatedAt DateTime @updatedAt }
After defining your models, generate the Prisma client:
npx prisma generate
Now that we have our database connection and models set up, let's look at how to perform CRUD (Create, Read, Update, Delete) operations in Remix.
In a loader or action function:
import { db } from "~/db.server"; export async function action({ request }: ActionArgs) { const formData = await request.formData(); const title = formData.get("title"); const content = formData.get("content"); const authorId = formData.get("authorId"); const post = await db.post.create({ data: { title, content, authorId: Number(authorId), }, }); return json({ post }); }
In a loader function:
import { db } from "~/db.server"; export async function loader({ params }: LoaderArgs) { const post = await db.post.findUnique({ where: { id: Number(params.id) }, include: { author: true }, }); if (!post) { throw new Response("Not Found", { status: 404 }); } return json({ post }); }
In an action function:
import { db } from "~/db.server"; export async function action({ request, params }: ActionArgs) { const formData = await request.formData(); const title = formData.get("title"); const content = formData.get("content"); const post = await db.post.update({ where: { id: Number(params.id) }, data: { title, content, }, }); return json({ post }); }
In an action function:
import { db } from "~/db.server"; export async function action({ params }: ActionArgs) { await db.post.delete({ where: { id: Number(params.id) }, }); return redirect("/posts"); }
As your application evolves, you'll need to update your database schema. Prisma makes this process straightforward:
schema.prisma
file with the new changes.npx prisma migrate dev --name add_user_role
npx prisma migrate deploy
To ensure your Remix application performs well with database operations, consider the following tips:
Here's an example of implementing pagination:
export async function loader({ request }: LoaderArgs) { const url = new URL(request.url); const page = Number(url.searchParams.get("page") || "1"); const perPage = 20; const posts = await db.post.findMany({ skip: (page - 1) * perPage, take: perPage, orderBy: { createdAt: "desc" }, }); const total = await db.post.count(); return json({ posts, pagination: { page, perPage, total, totalPages: Math.ceil(total / perPage), }, }); }
As you become more comfortable with databases in Remix, explore these advanced techniques:
Here's an example of a database transaction:
import { db } from "~/db.server"; export async function action({ request }: ActionArgs) { const formData = await request.formData(); const title = formData.get("title"); const content = formData.get("content"); const authorId = formData.get("authorId"); const post = await db.$transaction(async (prisma) => { const post = await prisma.post.create({ data: { title, content, authorId: Number(authorId), }, }); await prisma.user.update({ where: { id: Number(authorId) }, data: { postCount: { increment: 1 } }, }); return post; }); return json({ post }); }
This transaction ensures that both the post creation and user update occur atomically, maintaining data consistency.
27/01/2025 | RemixJS
27/01/2025 | RemixJS
27/01/2025 | RemixJS
27/01/2025 | RemixJS
27/01/2025 | RemixJS
27/01/2025 | RemixJS
27/01/2025 | RemixJS
27/01/2025 | RemixJS