This is the second article in a series dedicated to showing you how to use Prisma in your Nuxt 3 app and will look at creating your Prisma Schema.
Get notified when we release new tutorials, lessons, and other expert Nuxt content.
Trying to manage database schemas alongside your Nuxt app types can be a challenge.
But with Prisma, most of these problems go away.
It handles all of the boilerplate and coordination, so you just write one single schema that’s used in your database and in your TypeScript app.
This is the second article in a series dedicated to showing you how to use Prisma in your Nuxt 3 app:
If you’re unfamiliar with database schemas — and even if you are — getting used to Prisma schemas can take a bit of time.
But I promise that as we start to actually use the Prisma client, you’ll start to understand how this schema works even more clearly.
Might as well get on with it, then!
To keep things organized, we’ll create a /prisma
folder where all things Prisma will live. This is one of the locations that Prisma will look in by default.
Create a schema.prisma
file and add the following to it:
datasource db {
url = env("DATABASE_URL")
provider = "postgresql"
}
generator client {
provider = "prisma-client-js"
}
These two sections set everything up.
The datasource
configures the database connection itself. The generator
section tells Prisma how to generate the client. Currently, only Javascript is officially supported, but there are lots of community created generators that let you generate docs, Zod schemas, and all sorts of things.
One really important parameter to pay attention to is the binaryTargets
field in the generator
block:
generator client {
provider = "prisma-client-js"
binaryTargets = ["native", "darwin-arm64"]
}
Prisma will auto-detect which platform you’re on and will use the right binary, but sometimes you need to specify it. For example, if you build on one platform and then deploy your app on a separate platform, you’ll need to make sure that Prisma has built with the necessary binaries or it won’t run properly.
Again, usually this isn’t an issue, but it’s good to know in case this comes up.
You can find out more in the docs.
You can also change the location of where the generated client lives:
generator client {
provider = "prisma-client-js"
output = "somewhere/else/prisma/client"
}
In order to get Prisma to actually do something, we need to create models. These create our database tables and our TypeScript types.
Here, we’ll create some models for the course platform app that we build in Mastering Nuxt 3:
model Course {
id Int @id @default(autoincrement())
title String
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}
model Chapter {
id Int @id @default(autoincrement())
title String
slug String
number Int
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}
model Lesson {
id Int @id @default(autoincrement())
title String
slug String
number Int
downloadUrl String
videoId Int
text String
sourceUrl String?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}
There’s a lot you can do with models, so I highly recommend reading through the docs. But here’s a simple explanation.
It should look pretty similar to a Postgres schema, if you’re familiar with those. For each model, we define the field name, the type, and then whatever attributes that field has:
Field Type Attributes
-----------------------------
id Int @id @default(autoincrement())
title String
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
Prisma has special types that map to Postgres, MySQL, and other database platforms. You can also specify native database types directly if you need to.
The attributes are special modifiers. Here are the ones used for the Course
model:
@id
— this is the unique id
column for this table@default
— specifies the default value of the field@updatedAt
— automatically keeps track of the last time a record was updatedHere is the full list of attributes.
Our models are not completely independent though, and all the relationships between models is really the whole point of a relational database.
Each course contains multiple chapters, and each chapter multiple lessons.
We need to represent this in our schema in order for Prisma (and Supabase/Postgres) to keep things consistent and ensure our data always makes sense. Prisma does this through relations.
Here, we add a relation between the Chapter
and Lesson
models:
model Chapter {
id Int @id @default(autoincrement())
title String
slug String
number Int
lessons Lesson[]
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}
This line lessons Lesson[]
specifies that each Chapter
contains many Lesson
objects. So far so good.
If you’ve got the Prisma VS Code extension installed and configured like we did in the previous article, all we need to do now is hit save, and the Lesson
model will be updated to reflect this new relationship.
You can also update the Lesson
model yourself and add in these lines:
chapter Chapter @relation(fields: [chapterId], references: [id])
chapterId Int
Our Lesson
model now looks like this:
model Lesson {
id Int @id @default(autoincrement())
title String
slug String
number Int
downloadUrl String
videoId Int
text String
sourceUrl String?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
chapter Chapter @relation(fields: [chapterId], references: [id])
chapterId Int
}
These two fields we added do the following:
chapterId
— Not only does the Chapter
keep track of which Lesson
objects it has, but each Lesson
also needs to keep track of which Chapter
it belongs to. This is what this field is for.chapter
— This line tells Prisma exactly how this relationship operates. We’re saying that the chapterId
field on the Lesson
model is linked to the id
field on the Chapter
model.Now these two objects are linked!
It’s important to note that we added three fields in order to set up these relationships, but only the chapterId
field will actually be created in our database. The other two fields are there to help Prisma hook everything up correctly, and are available in the client we’ll use in our TypeScript code.
If we follow a similar process to link our Course
and Chapter
models, we end up with this schema:
datasource db {
url = env("DATABASE_URL")
provider = "postgresql"
}
generator client {
provider = "prisma-client-js"
}
model Course {
id Int @id @default(autoincrement())
title String
chapters Chapter[]
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}
model Chapter {
id Int @id @default(autoincrement())
title String
slug String
number Int
lessons Lesson[]
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
Course Course @relation(fields: [courseId], references: [id])
courseId Int
}
model Lesson {
id Int @id @default(autoincrement())
title String
slug String
number Int
downloadUrl String
videoId Int
text String
sourceUrl String?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
chapter Chapter @relation(fields: [chapterId], references: [id])
chapterId Int
}
The Prisma schema is the heart of what makes Prisma so great.
We write one file that generates our database schema and all of the types for our app, making it so much easier to manage.
And Prisma comes with a ton of really great features — I highly recommend you check out the docs to see all of the different things you can do with it.
We cover Prisma + Nuxt 3 in more detail in the Mastering Nuxt 3 course if you want to learn more.
Next Article: Seeding the Database with Dummy Data