back to posts.

What is ORM?

Oğulcan Bozkurt / 4 Eylül 2024

What is ORM?

  • ORM is basically a piece of software designed to translate database tables into classes, structs, types and so on.
  • It will be a bridge between two incompatible types.
  • Since it promises that the data fetched from the database will have the same attributes as the one used in the current workspace, it will enhance the developer experience.

Prisma ORM

  • The ORM I currently use. With Prisma CLI, it is fairly so easy to create database tables. It allows you to do pull or push operations to your database, within seconds.
model User {
  id      String      @id @default(autoincrement())
  email   String   @unique
  name    String?
  role    Role     @default(USER)
  posts   Post[]
  profile Profile?

  @@map("users")
}

model Profile {
  id     String    @id @default(autoincrement())
  bio    String
  user   User   @relation(fields: [userId], references: [id])
  userId String    @unique

  @@map("profiles")
}
  • It's a simple prisma.schema file. You can easily create models, define fields and also give them default values.
  • If it references another model's field, you can implicityly create foreign keys, and define the relations.
// model profile
user   User   @relation(fields: [userId], references: [id])
userId Int    @unique

In the profile table, you make sure that the userId is a foreign key, which is related with user table's id field.

What's next?

After creating the tables and relations between them, you can basically create a prisma client.

import { PrismaClient } from '@prisma/client'

const prismaClientSingleton = () => {
  return new PrismaClient()
}

declare const globalThis: {
  prismaGlobal: ReturnType<typeof prismaClientSingleton>;
} & typeof global;

const prisma = globalThis.prismaGlobal ?? prismaClientSingleton()

export default prisma

if (process.env.NODE_ENV !== 'production') globalThis.prismaGlobal = prisma
  • So then, prisma will create every model in your prisma.schema file, as a type, so you can use your tables anywhere in the application.

How to fetch data?

// e.g. in `pages/index.tsx`
import prisma from './db'

export const getServerSideProps = async () => {
  const posts = await prisma.post.findMany()

  return { props: { posts } }
}
  • This example uses old version of Next.js, where you use getServerSideProps.
  • But In never versions, you can use server components to do async work.
// e.g. in `actions/getPosts.ts`

export const getPosts = async () => { 
  try {
    const posts = await prisma.post.findMany();

    return {
      message: "Succes.."
      status: 200,
      posts
    }

  } catch {
    return {
      status: 500,
      message: "Server error"
    }
  }
}

In your page.tsx, you can call this funtion and it will automatically will be understood by typescript that it's typed as Posts[].

How to make join operations in prisma?

Okay so, imagine If we want to fetch the profile, but also want to reach to the user's email.

Profile table, doesn't know if the User has a email or not. We need a join operation to get access to the fields of the User table.

// e.g. in `actions/getProfileById.ts`

const getProfileById = async (id: string) {

  try {
    const profile = await prisma.profile.findFirst({
      where: {
        id
      },
      include: { // we are making sure to include the user.
        user: true
      }
    });
    return {
      message: "Succes.."
      status: 200,
      profile
    }
  } catch {
    return {
      status: 500,
      message: "Server error"
    }
  }
}
  • After including the user, we can have access to the fields of User table.
// e.g. in page.tsx

export const MainPage = async () => {

  const profileResponse = getProfileById("can.bozkurt");

  if(!profileResponse.profile) {
    return (
      <div>somethng went wrong...</div>
    )
  }

  return(
    <div>
      hello
      {profile.user.email} // ts error! 
    </div>
  )

}
  • However, there is one problem. By default, when we fetch this data in our page.tsx, typescript won't understand profile.user.email.
  • We need to create a type, where it will recognise that the Profile will now have a user field.
import { User } from "@lib/prisma"
export type DetailedProfile = {
  //... default fields of profile
  id: string,
  bio: string

  user: User // !
}

Now, in our page.tsx, we can use the user field.

// e.g. in page.tsx

export const MainPage = async () => {

  const profileResponse = getProfileById("can.bozkurt");

  if(!profileResponse.profile) {
    return (
      <div>somethng went wrong...</div>
    )
  }

  const detailedProfile: DetailedProfile = profileResponse.profile

  return(
    <div>
      hello
      {profile.user.email} // no error :)
    </div>
  )
}

Thank you for hanging out and learn something new.