Getting started with Prisma ORM

Getting started with Prisma ORM

ยท

9 min read

Before we jump in , we should deal with the basics first.

The Basics

What is an ORM?

Object Relational Mapping (ORM) is a technique used in creating a "bridge" between object-oriented programs and, in most cases, relational databases . You will get to know more about what they mean by it as we progress further .

There are many ORMs available in the market and the one which is slowly becoming a market leader is Prisma .

What is Prisma ?

As given on the official website ,

Prisma unlocks a new level of developer experience when working with databases thanks to its intuitive data model, automated migrations, type-safety & auto-completion.

If you remember that in my "Getting started with SQL Databases" blog , I mentioned about the shortcomings of writing raw SQL queries which were basically you have to know SQL , can't get types and migrations being difficult and those are the exact problems which are being solved here .

Prisma ORM is going to be simply a layer before your database and through this you can connect any database like Postgres , MongoDB and so on .

We will need hands-on practise on it so first lets grab a remote postgres url.

Grabbing remote Postgres URL from providers like Neon and Supabase

For using Prisma in your project , you will need to give the access of your Postgres database to it . You won't be able to use ElephantSQL since what happens is that your database is divided into sub-databases , and the access of one such sub-database is given to you and rest is paid . So the best option available to us is use some other provider like Neon or Supabase . Just create an account , create an instance and grab the URL in your clipboard . Now we are ready to get our hands dirty.

Lets get our hands dirty now

You can clone the repo:

https://github.com/career-tokens/PrismaTutorial

Then run:

npm i

To install the dependencies . Do it for getting a headstart or you can simply follow along with me . We would be taking help from the official docs as well tho there will be some minor changes otherwise its always better to be able to read the docs and accordingly go about dealing with the project .

Creating TypeScript project and set up Prisma

Create a folder manually and open it with VSCode (thats the editor which I am going to use) .

Next, lets initialize a TypeScript project using npm:

npm init -y
npm install typescript ts-node @types/node --save-dev

This will create a package.json file with an initial setup for your TypeScript app .

{
  "name": "prismatutorial",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "@types/node": "^20.11.16",
    "ts-node": "^10.9.2",
    "typescript": "^5.3.3"
  }
}

Now, initialize TypeScript:

npx tsc --init

Then, install the Prisma CLI as a development dependency in the project:

npm install prisma --save-dev

Now the next command won't be exactly same as given in the official quickstart documentation . They are going to use sqlite database but we will need postgres and so simply replace sqlite with postgresql .

 npx prisma init --datasource-provider postgresql

This will create a new prisma directory with a Prisma schema file and configures PostgreSQL as the database. We are now ready to model our data and configure our database with some tables .

๐Ÿ’ก
In the schema.prisma file , I am sure you have noticed this line url = env("DATABASE_URL") . This simply means that you are supposed to use extract the postgres url from the .env file . So in the .env file , replace the garbage value (for DATABASE_URL) with the actual postgres url (which you grabbed in the beginning) .

Now we will need to give the models for our data .

Model your data in your schema.prisma file

We are going to add some models to the schema.prisma file .
The models would represent a generalisation of users and their posts . We will also see how we relate an user to his/her post and how a post is related to its author .

The comments alongwith the code are self-explanatory and am sure you were able to grasp the concept . Our next target is run migrations .

๐Ÿ’ก
You can install "Prisma" extension to highlight the file with different colors .

Running migration to create tables in database

Run this command to create the "User" and "Post" tables in the database:

npx prisma migrate dev --name init

This command did three things:

  1. It created a new SQL migration file for this migration in the prisma/migrations directory where you dive deep into then you would be able to find raw SQL queries to create table (it made our life easier by writing the equivalent SQL itself).

  2. It executed the SQL migration file against the remote database.

  3. It ran prisma generate under the hood (which installed the @prisma/client package and generated a tailored Prisma Client API based on your models).

๐Ÿ’ก
Another use case is that if you make a change to the tables or create another table in the "schema.prisma" file then on re-running the previous command will simply generate another migration folder which would contain raw SQL file to make those changes. Hence Prisma is really suitable for production level changes .

Congratulations, you now have your database and tables ready. Let's go and learn how we can send some queries to read and write data!

Sending queries with Prisma Client

But first we will need to make some changes . Go to "tsconfig.json" file and make these changes:

Basically uncomment "rootDir" and "outDir" and add those values . Now let's send our first query where we are going to add an user to the User table.

Adding an user to the User table

Now create a "src" folder in the project and add a "create-user.ts" file in it.
Lets see the code we would write in it .


import { PrismaClient } from '@prisma/client'

const prisma = new PrismaClient();//we need to use prisma client

async function main() {
//creating a query to to add a row to the User table with email
//and name's value given
  await prisma.user.create({
    data:{
        email:"abc@gmail.com",
        name:"abc"
    }
  })
}
//once the query succeeds or fails we disconnect the client
main()
  .then(async () => {
    await prisma.$disconnect()
  })
  .catch(async (e) => {
    console.error(e)
    await prisma.$disconnect()
    process.exit(1)
  })
//the prototype of the code is taken from the docs

Now run:

tsc -b

This would convert the TS files to JS files and store them in "dist" folder .
Now run:

node dist/create-user.js

Now to visualise the current tables you can run:

npx prisma studio

It will turn on the studio on a local port and you can view your tables presented on a nice UI .

๐Ÿ’ก
While writing the code , you would also be able to see suggestions , and thats another use case of Prisma .

Let's move to the next segment .

Adding a post to the Post table

Now lets add a post to the Post table and for that lets create a "create-post.ts" file and then paste this code .


import { PrismaClient } from '@prisma/client'

const prisma = new PrismaClient()
//query to add a post where the title is "my blog",content is "prisma" and its author is 
//the same user (from User table) who has the id==1
async function main() {
  await prisma.post.create({
    data:{
        title:"my blog",
        content:"prisma",
        author:{
            connect:{
                id:1
            }
        }
    }
  })
}

main()
  .then(async () => {
    await prisma.$disconnect()
  })
  .catch(async (e) => {
    console.error(e)
    await prisma.$disconnect()
    process.exit(1)
  })

Lets run prisma studio again and then we should be seeing this:

Post table got updated and on viewing it we can see :

And since the author of the post is the user with the id==1 in the User table , on clicking author you can see this :

๐Ÿ’ก
You can see the raw SQL queries in the logs by using :
const prisma=new PrismaClient({log:['info','query']})// at the top

After adding both post and user , our next job should be to fetch the users.

Fetching users from the User table

Lets create a file named "get-users.ts" and paste this code:

import { PrismaClient } from '@prisma/client'

const prisma = new PrismaClient()

async function main() {
//query to fetch all the users
  const users = await prisma.user.findMany({})
  //you could also write 
//   await prisma.user.findMany({
//     where:{
//         email:"main@gmail.com"
//     }
//   })
//such that you could have got the users with the same mail
  console.log(users)
  //here we are talking about a specific user , the above feature returned an array rather
  const user=await prisma.user.findUnique({
    where:{
        id:1
    },
    include:{
        posts:true
    }
  });
  console.log(user)
}

main()
  .then(async () => {
    await prisma.$disconnect()
  })
  .catch(async (e) => {
    console.error(e)
    await prisma.$disconnect()
    process.exit(1)
  })

I hope you can understand what I meant , its pretty straightforward tho.

Now since we have already added the user , we may need to change the user in the future so lets learn how to do that.

Updating the user

Lets create "update-user.ts" file and paste this code :


import { PrismaClient } from '@prisma/client'

const prisma = new PrismaClient()

async function main() {
    //query to update the user with the id==1 such that the name should be changed to "mainak"
  await prisma.user.update({
    where: {
        id: 1
    },
    data: {
        name: "mainak"
    }
  })
}

main()
  .then(async () => {
    console.log("done");
    await prisma.$disconnect()
  })
  .catch(async (e) => {
    console.error(e)
    await prisma.$disconnect()
    process.exit(1)
  })

Pretty straightforward . Now lets also understand how to delete an user .

Deleting user

Lets create a file "delete-user.ts" and paste this code:


import { PrismaClient } from '@prisma/client'

const prisma = new PrismaClient()

async function main() {
    //query to delete the user with the id==1 
  await prisma.user.delete({
    where: {
        id: 1
    },
  })
}

main()
  .then(async () => {
    console.log("done");
    await prisma.$disconnect()
  })
  .catch(async (e) => {
    console.error(e)
    await prisma.$disconnect()
    process.exit(1)
  })

Now lets also see some advanced variation to it . In this code we would try to update the user in such a way that we would be removing such posts of the user which are unpublished . First create a file named "delete-unpublished-posts.ts" and then paste this code (make sure to see the comments):


import { PrismaClient } from '@prisma/client'

const prisma = new PrismaClient()

async function main() {
//query to delete as many posts as possible which are basically
//unpublished for the user with id==1
  await prisma.user.update({
    where: {
        id: 1
    },
    data: {
        posts: {
            deleteMany: {
                published: false
            }
        }
    }
  })
}

main()
  .then(async () => {
    console.log("done");
    await prisma.$disconnect()
  })
  .catch(async (e) => {
    console.error(e)
    await prisma.$disconnect()
    process.exit(1)
  })

Its all about the syntax and I am pretty sure you were able to understand it .

Wrapping Up

Prisma is kinda equivalent to Mongoose . You can also use it alongwith MongoDB . Maybe thats a blog for another day . For now I hope you were able to understand whatever was discussed . It might not have been a smooth ride but if you are patient you will be able to understand the stuff . So thats it , thank you for reading !

ย