Here I dive in Prisma. Discussing the basics of what Prisma is, the functionality and how it could be intergrated into a node.js application.
Essentially itβs an ORM for Node.js and TypeScript. Whatβs an ORM you ask? Well, ORM stands for object relational model, this means it can connect your application to a database.
By creating a prisma schema, a bit like a database schema, here you can define a database model and relations. To connect a database you will need a prisma client. To update your schema, you will need to run the prisma mirgrate command: npx prisma migrate dev. prisma studio is an admin UI to view and edit data inside your database.
This is the main config file. In your application it will be called schema.prisma.
When a prisma command is called, the cli reads information from the schema. There are two examples of when this may be invooked:
prisma generate: reads all information from the schema to generate the correct data source for prisma clientprisma migrate dev: reads all data sources and data model defintion to create a new migrationYou can also use environment variables within schema files to provide config for CLI command.
A data source determines how Prisma connects to a database.
Within the schema the following will be able to determine this.
datasource db {
provider = "mongodb"
url = env("DATABASE_URL")
}
Prisma can only have one data source but the data source url can be overridden with prisma client.
Prisma resolves SSL certificates within the ./prisma directory, so all certificates files should be within that folder.
A schema can have one or more generators. When the prisma generate command is rum, the generator determines assets created.
enerator client {
provider = "prisma-client-js"
output = "./generated/prisma-client-js"
}
This defines the application models, which are also known as prisma models.
Models represetn the entites of the application, map to tables/collections(MongoDB) within a database and form the foundation of the queries available in the generated prisma client api.
model User {
id String @id @default(auto()) @map("_id") @db.ObjectId
createdAt DateTime @default(now())
name String?
email String @unique
role Role @default(USER)
}
One record can be connect to both sides of the relation.
model User {
id String @id @default(auto()) @map("_id") @db.ObjectId
profile Profile?
}
model Profile {
id String @id @default(auto()) @map("_id") @db.ObjectId
user User @relation(fields: [userId], references: [id])
userId String @unique @db.ObjectId // relation scalar field (used in the `@relation` attribute above)
}
model User {
id Int @id @default(autoincrement())
profile Profile? // No relation scalar - must be optional
}
One relation on onside of the relation can be connected to zero or more records on the other side.
model User {
id String @id @default(auto()) @map("_id") @db.ObjectId
posts Post[]
}
model Post {
id String @id @default(auto()) @map("_id") @db.ObjectId
author User @relation(fields: [authorId], references: [id])
authorId String @db.ObjectId
}
Zero or more records on one side of the relation can be connected to zero or more records on the other side.
model Post {
id Int @id @default(autoincrement())
title String
categories CategoriesOnPosts[]
}
model Category {
id Int @id @default(autoincrement())
name String
posts CategoriesOnPosts[]
}
model CategoriesOnPosts {
post Post @relation(fields: [postId], references: [id])
postId Int // relation scalar field (used in the `@relation` attribute above)
category Category @relation(fields: [categoryId], references: [id])
categoryId Int // relation scalar field (used in the `@relation` attribute above)
assignedAt DateTime @default(now())
assignedBy String
@@id([postId, categoryId])
}
CREATE TABLE "Post" (
"id" SERIAL NOT NULL,
"title" TEXT NOT NULL,
CONSTRAINT "Post_pkey" PRIMARY KEY ("id")
);
CREATE TABLE "Category" (
"id" SERIAL NOT NULL,
"name" TEXT NOT NULL,
CONSTRAINT "Category_pkey" PRIMARY KEY ("id")
);
-- Relation table + indexes --
CREATE TABLE "CategoriesOnPosts" (
"postId" INTEGER NOT NULL,
"categoryId" INTEGER NOT NULL,
"assignedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"assignedBy" TEXT NOT NULL,
CONSTRAINT "CategoriesOnPosts_pkey" PRIMARY KEY ("postId","categoryId")
);
ALTER TABLE "CategoriesOnPosts" ADD CONSTRAINT "CategoriesOnPosts_postId_fkey" FOREIGN KEY ("postId") REFERENCES "Post"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
ALTER TABLE "CategoriesOnPosts" ADD CONSTRAINT "CategoriesOnPosts_categoryId_fkey" FOREIGN KEY ("categoryId") REFERENCES "Category"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
datasource db {
provider = "mongodb"
url = env("DATABASE_URL")
}
generator client {
provider = "prisma-client-js"
}
model User {
id String @id @default(auto()) @map("_id") @db.ObjectId
createdAt DateTime @default(now())
name String?
email String @unique
role Role @default(USER)
}
enum Role {
USER
}
Although the default schema name is schema.prisma, if it has a different name you can do the following: prisma generate --schema ./database/myschema.prisma
To generate a prisma client, you will first need to have set up a prisma schema file, with a generator and at least one model.
Install Prisma CLI
npx install prisma --save-dev
npx prisma
Install Prisma Client in your project
npm install @prisma/client
Use Prisma Client to send queries to your database
import { PrismaClient } from '@prisma/client'
const prisma = new PrismaClient()
// use `prisma` in your application to read and write data in your DB
// run inside `async` function
const newUser = await prisma.user.create({
data: {
name: 'example',
email: 'example@prisma.io',
},
})
const users = await prisma.user.findMany()
When you make changes to your database, which are reflect in the schema, you need to re-generate the Prisma client to update the code within the Node modules for prisma client.
prisma generate
Does not apply to MongoDB. Instead of migrate dev, use db push for MongoDB.
Create the first migration
prisma migrate dev --name init
The schema should be in sync with your database schema and here is the initialised migration history:
migrations/
ββ 20210313140442_init/
ββ migration.sql
Now add any additional fields to the schema
model User {
id Int @id @default(autoincrement())
jobTitle String
name String
posts Post[]
}
Create the second migration
prisma migrate dev --name added_job_title
Here is what the migration history contains:
migrations/
ββ 20210313140442_init/
ββ migration.sql
ββ 20210313140442_added_job_title/
ββ migration.sql