
Prisma is an open-source database toolkit and Object-Relational Mapping (ORM) tool.
What’s Prisma
Prisma provides a set of tools and libraries that simplify database access and management for developers. Prisma supports various databases, including PostgreSQL, MySQL, SQLite, and SQL Server.
Here are some key features and concepts of Prisma:
- Database Modeling: Prisma allows you to define your database schema using its own declarative language called Prisma Schema. You can define entities, relationships, fields, and other aspects of your database structure.
- Type-Safe Database Access: Prisma generates a type-safe and auto-completed database client based on your Prisma Schema. This client provides a set of methods for querying, creating, updating, and deleting data in your database. It leverages TypeScript or JavaScript to provide compile-time type checking and autocompletion, reducing the chances of runtime errors.
- Database Migrations: Prisma includes a migration system that helps you manage changes to your database schema over time. It tracks and applies migrations to keep your database schema in sync with your Prisma Schema.
- Query Language: Prisma provides a powerful and expressive query language called Prisma Client Query API. It allows you to write complex database queries in a concise and readable manner, supporting filtering, sorting, pagination, and other query operations.
- Real-Time Data Sync: Prisma supports real-time data synchronization through its integration with GraphQL subscriptions. You can subscribe to changes in your database and receive real-time updates in your application.
- Prisma Client: Prisma generates a client library specific to your database schema. This client library acts as an interface between your application and the database, providing a convenient and type-safe API to interact with the database.
Prisma is often used in modern web development stacks, alongside frameworks like Next.js, Express, or GraphQL. It simplifies database access, improves developer productivity, and helps maintain a clean and scalable data layer in your application.
Quick start on Prisma
Create project and setup
1 | mkdir hello-prisma |
As a first step, create a project directory and navigate into it. Then the code executed in the terminal using npm, the package manager for Node.js. Let’s break down each command:
npm init -y: This command initializes a new npm project in the current directory. The-yflag automatically accepts the default options for the project initialization, such as the package name, version, entry point, and license. It generates apackage.jsonfile that holds metadata about the project and its dependencies.npm install typescript ts-node @types/node --save-dev: This command installs several packages as development dependencies in the project. Here’s what each package does:typescript: This package installs the TypeScript compiler, which allows you to write and transpile TypeScript code into JavaScript.ts-node: This package provides a TypeScript execution environment for Node.js. It allows you to directly run TypeScript files without explicitly compiling them to JavaScript first.@types/node: This package provides TypeScript type definitions for Node.js. It enables TypeScript to understand and provide type checking for Node.js-specific modules and APIs.- The
--save-devflag indicates that these packages should be saved as development dependencies in thepackage.jsonfile. Development dependencies are packages required during the development process but not necessary for the production deployment of the application.
By running these commands, you set up a new npm project, install TypeScript and related tools, and configure your project to use TypeScript for development.
1 | npx tsc --init |
npx tsc --init: This command initializes a TypeScript project in the current directory. It generates atsconfig.jsonfile that contains configuration options for the TypeScript compiler (tsc). The--initflag tellstscto create a defaulttsconfig.jsonfile with basic settings. You can further customize this file to suit your project’s needs.npm install prisma --save-dev: This command installs the Prisma package as a development dependency in the project. The--save-devflag indicates that the package should be saved in thedevDependenciessection of thepackage.jsonfile. Prisma is a toolkit for working with databases, and installing it as a development dependency means it’s not required for the production deployment of the application.npx prisma init --datasource-provider sqlite: This command initializes a Prisma project in the current directory. It sets up the necessary files and configurations for using Prisma in your project. The--datasource-provider sqliteflag specifies that you want to use SQLite as the data source for your Prisma project. Prisma will generate the required files and configurations to connect to a SQLite database.
By running these commands, you set up a TypeScript project, install Prisma as a development dependency, and initialize a Prisma project with SQLite as the data source. This allows you to leverage Prisma’s features and tools for working with databases, such as defining your database schema, generating a type-safe database client, and managing database migrations.
Model data in Prisma
The Prisma schema provides an intuitive way to model data. Add the following models to your schema.prisma file:
1 | model User { |
This code is written in Prisma Schema Language, which is used to define the database schema and models for a Prisma project. Let’s explain User first:
model User: This declares a model named “User” representing a user entity in the database.id Int @id @default(autoincrement()): This defines anidfield of typeIntas the primary key for the “User” model. The@idattribute specifies that this field is the primary identifier. The@default(autoincrement())attribute indicates that the field should automatically increment its value for each new record.email String @unique: This defines anemailfield of typeStringin the “User” model. The@uniqueattribute ensures that each email value in the database is unique.name String?: This defines a nullablenamefield of typeStringin the “User” model. The?denotes that the field can be optional and may contain a null value.posts Post[]: This establishes a one-to-many relationship between the “User” and “Post” models. It indicates that a user can have multiple posts. ThePost[]syntax represents an array ofPostobjects associated with the user.
The the Post:
model Post: This declares a model named “Post” representing a post entity in the database.id Int @id @default(autoincrement()): This defines anidfield of typeIntas the primary key for the “Post” model, similar to the “User” model.title String: This defines atitlefield of typeStringin the “Post” model, representing the title of the post.content String?: This defines a nullablecontentfield of typeStringin the “Post” model, representing the content of the post.published Boolean @default(false): This defines apublishedfield of typeBooleanin the “Post” model. The@default(false)attribute sets the default value of the field tofalse.author User @relation(fields: [authorId], references: [id]): This establishes a many-to-one relationship between the “Post” and “User” models. It indicates that a post belongs to a single user. The@relationattribute specifies the relationship, andfields: [authorId]andreferences: [id]define the fields used for establishing the relationship.authorId Int: This defines anauthorIdfield of typeIntin the “Post” model. It represents the foreign key that references theidfield of the associated user in the “User” model.Run migration to create database tables
At this point, you have a Prisma schema but no database yet. Run the following command in your terminal to create the SQLite database and the User and Post tables represented by your models:
1 | npx prisma migrate dev --name init |
Let’s explain it:
npx: This is a utility that allows you to run a package without installing it globally. It executes the following command using the locally installed version of the package.prisma: This is the command for invoking the Prisma CLI.migrate dev: This command is used to apply pending database migrations. It ensures that the database schema is up to date with the latest changes defined in your Prisma schema file.--name init: This flag specifies the name of the migration. In this case, it is set to “init”. The name is used to identify the migration and can be helpful for tracking and managing migrations in your project.
When you run the provided code, the Prisma CLI will perform the following steps:
- It will check for any pending migrations that have not been applied to the database.
- If there are pending migrations, it will generate the necessary SQL statements to apply those migrations to the database. These migrations typically involve creating or modifying database tables, columns, or other schema changes defined in your Prisma schema file.
- It will execute the generated SQL statements to apply the migrations to the database, ensuring that the database schema is synchronized with your Prisma schema.
- Once the migrations are applied, the Prisma CLI will update the migration history, marking the applied migrations as completed.
Send queries to your database with Prisma Client
To send queries to the database, you will need a TypeScript file to execute your Prisma Client queries. Create a new file called script.ts for this purpose:1
touch script.ts
Paste the following boilerplate into it:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17import { PrismaClient } from '@prisma/client';
const prisma = new PrismaClient();
async function main() {
// ... you will write your Prisma Client queries here
}
main()
.then(async () => {
await prisma.$disconnect()
})
.catch(async (e) => {
console.error(e)
await prisma.$disconnect()
process.exit(1)
})
The code:
- Imports the
PrismaClientclass from the@prisma/clientpackage. ThePrismaClientis the main entry point for interacting with your database using Prisma. - Creates an instance of the
PrismaClientclass and assigns it to theprismavariable. This instance represents a connection to your database and provides methods for executing queries and mutations. - Defines an
asyncfunction namedmain(). This function serves as the entry point for executing Prisma Client queries and mutations. You can write your Prisma Client code inside this function. - Then
- Calls the
main()function and handles the execution flow. It uses a combination ofthen()andcatch()to handle successful execution and error scenarios. - If the
main()function resolves successfully, thethen()block is executed. Inside thethen()block,await prisma.$disconnect()is called to close the database connection and release any resources held by Prisma Client. - If an error occurs during the execution of
main(), thecatch()block is executed. The error is logged to the console usingconsole.error(e). Then,await prisma.$disconnect()is called to close the database connection, andprocess.exit(1)is used to exit the Node.js process with a non-zero status code (indicating an error).
- Calls the
Create new User record
Add the following code to your script.ts file:1
2
3
4
5
6
7
8
9async function main() {
const user = await prisma.user.create({
data: {
name: 'Alice',
email: 'alice@prisma.io',
},
})
console.log(user)
}
Next, execute the script with the following command:1
npx ts-node script.ts
You just created your first database record with Prisma Client!
Retrieve all User records
Prisma Client offers various queries to read data from your database. In this section, you’ll use the findMany query that returns all the records in the database for a given model.
Delete the previous Prisma Client query and add the new findMany query instead:1
2
3
4async function main() {
const users = await prisma.user.findMany()
console.log(users)
}
Then:1
npx ts-node script.ts
Notice how the single User object is now enclosed with square brackets in the console. That’s because the findMany returned an array with a single object inside.
Explore relation queries with Prisma
One of the main features of Prisma Client is the ease of working with relations. In this section, you’ll learn how to create a User and a Post record in a nested write query. Afterwards, you’ll see how you can retrieve the relation from the database using the include option.
First, adjust your script to include the nested query:1
2
3
4
5
6
7
8
9
10
11
12
13
14sync function main() {
const user = await prisma.user.create({
data: {
name: 'Bob',
email: 'bob@prisma.io',
posts: {
create: {
title: 'Hello World',
},
},
},
})
console.log(user);
}
Run the query by executing the script again:1
npx ts-node script.ts
By default, Prisma only returns scalar fields in the result objects of a query. That’s why, even though you also created a new Post record for the new User record, the console only printed an object with three scalar fields: id, email and name.
In order to also retrieve the Post records that belong to a User, you can use the include option via the posts relation field:1
2
3
4
5
6
7
8...
const usersWithPosts = await prisma.user.findMany({
include: {
posts: true,
},
})
console.dir(usersWithPosts, { depth: null })
...
Run the script again to see the results of the nested read query.