Prisma with Nuxt 3: Creating the Prisma Schema (2 of 5)

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.

Michael Thiessen
Nuxt 3

Mastering Nuxt 3 course is here!

Get notified when we release new tutorials, lessons, and other expert Nuxt content.

Click here to view the Nuxt 3 course

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:

  1. Setting up Prisma (with Supabase)
  2. Creating the Prisma Schema 👈 we’re here
  3. Seeding the Database with Dummy Data
  4. Getting Data from our Database with Prisma
  5. Modifying Data using Prisma

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!

Creating our schema file

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.

Binary Targets

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.

Output Location

You can also change the location of where the generated client lives:

generator client {
  provider = "prisma-client-js"
  output   = "somewhere/else/prisma/client"
}

Adding Models

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 updated

Here is the full list of attributes.

Adding Model Relations

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
}

Conclusion

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

Michael Thiessen
Michael is a passionate full time Vue.js and Nuxt.js educator. His weekly newsletter is sent to over 11,000 Vue developers, and he has written over a hundred articles for his blog and VueSchool.

Follow MasteringNuxt on