Relational Mapping
Check this guide (opens in a new tab) to learn more about Prisma and ORM.
Database + Schema
Install the Prisma extension (opens in a new tab) for Visual Studio Code from the marketplace.
-
Create a Next.js application using
bunx create-next-app@latest.1 -
Install the Prisma package using
bun add --dev prisma dotenv. -
Initialize Prisma with SQLite (opens in a new tab) as data source provider using
prisma init --datasource-provider sqlite --generator-provider prisma-client-js --output client.2 Renameprisma.config.tstoprisma.config.mjs. -
The environment variable
DATABASE_URLin.envcan be updated with the desired location of the database file, for example,DATABASE_URL="file:prisma/db/dev.db". -
Update the schema,
prisma/schema.prisma, with data models for a one-to-many relationship between users and their posts:- Each
Userhas anidentifier, a uniqueemail, aname, and a list ofposts. - Each
Posthas anidentifier, atitle, a textcontent, apublishedstatus, and a correspondingauthor.
prisma/schema.prismamodel User { id String @id @default(cuid(2)) email String @unique name String? posts Post[] } model Post { id String @id @default(cuid(2)) title String content String? published Boolean @default(false) postAuthor User @relation(fields: [author], references: [id]) author String } - Each
-
Create the supporting SQLite database using
prisma migrate dev --name init. This will generate and execute a SQL migration3 against the database4 to define its tables and relationships. -
Use Prisma Studio,
prisma studio, to view the database tables and edit/filter their records, if needed.
Client + Repository
-
Create a repository module,
repos/users.js, to manage access to the data. -
Install5 the client library and adapter using
bun add @prisma/client @prisma/client-runtime-utils @prisma/adapter-libsqland use them to access the data in the repository:repos/users.jsimport { PrismaLibSql } from "@prisma/adapter-libsql"; import { PrismaClient } from "@/prisma/client/client"; const prisma = new PrismaClient({ adapter: new PrismaLibSql({ url: process.env.DATABASE_URL ?? "", }), });The client is custom-tailored to the data model and must be regenerated using
prisma generatewhenever the schema is updated6. -
Create a
disconnect()method that closes a client connection, which will be used after having executed a query:repos/users.jsasync function disconnect() { try { await prisma.$disconnect(); } catch (e) { console.error(e); await prisma.$disconnect(); process.exit(1); } } -
Create a
read()method that returns all users along with their posts. It should calldisconnect()after executing the queries and before returning the results. Useprisma.user.findMany()and include the posts:repos/users.jsexport async function read() { const users = await prisma.user.findMany({ include: { posts: true, }, }); await disconnect(); return users; }The generated SQL queries can be logged by setting the
logoption in thePrismaClientconstructor:new PrismaClient({ log: ["query"] }). -
In a long-running application,
prisma.$disconnect()should not be explicitly called. One approach is to avoid callingdisconnect()and instead create a client module,repos/prisma.js, then import it inrepos/users.jsto cache thePrismaClientinstance and reuse it across the application:repos/prisma.jsimport { PrismaLibSql } from "@prisma/adapter-libsql"; import { PrismaClient } from "@/prisma/client/client"; export default new PrismaClient({ adapter: new PrismaLibSql({ url: process.env.DATABASE_URL ?? "", }), log: ["query"], });repos/users.jsimport prisma from "@/repos/prisma"; export async function read() { return await prisma.user.findMany({ include: { posts: true, }, }); }
RESTful API
-
Import and use the repository in
api/users/route.jsto implement aGETmethod that returns all users along with their posts usingread():app/api/users/route.jsimport * as users from "@/repos/users.js"; export async function GET(request) { return Response.json(await users.read()); } -
Test the route using Postman (opens in a new tab).
Seeding
-
Create a
prisma/seed.mjsscript that uses the Faker (opens in a new tab) package7 to procedurally generate sample data and populates the database usingprisma.user.create():prisma/seed.mjsimport { PrismaLibSql } from "@prisma/adapter-libsql"; import { PrismaClient } from "@/prisma/client/client"; import { faker } from "@faker-js/faker"; const prisma = new PrismaClient({ adapter: new PrismaLibSql({ url: process.env.DATABASE_URL ?? "", }), log: ["query"], }); const seed = async () => { Array.from({ length: Math.floor(Math.random() * 60) }).forEach( async () => await prisma.user.create({ data: { email: faker.internet.email(), name: faker.person.fullName(), posts: { create: Array.from({ length: Math.floor(Math.random() * 12), }).map(() => ({ title: faker.commerce.productName(), content: faker.lorem.text(), published: Math.random() > 0.5, })), }, }, }), ); }; try { await seed(); await prisma.$disconnect(); } catch (e) { console.error(e); await prisma.$disconnect(); process.exit(1); } -
Add a new entry under
migrationsinprisma.config.mjsthen manually seed the database usingprisma db seed:prisma.config.mjs{ ••• "migrations": { "seed": "bun prisma/seed.mjs" } } -
Verify that the data has been added using Prisma Studio or by sending a
GETrequest to the API route.
Footnotes
-
This guide is based on the Prisma ORM / Get Started / Quickstart (opens in a new tab) guide. ↩
-
Use
bunx prisma [command]for all Prisma commands. ↩ -
prisma db pushcan be used instead if migrations are not needed. ↩ -
prisma db pullcan be used instead with a pre-existing database. ↩ -
prisma migratedoes not perform this step. ↩ -
Install Prisma DBML Generator (opens in a new tab) to automatically generate DBML (opens in a new tab) diagrams. ↩
-
Install the Faker package using
bun add --dev @faker-js/faker. ↩