guide 006

How to manually set table names in Prisma and why you should

Since starting to work at Crunchy Data I have learned more about databases than I have my entire career.

Last week when showing off one of my projects backed by Prisma I got some dropped jaws when they looked at the database...😅

All across Prisma docs, tutorials, and content in the wild you will run into naming that looks like this for your models and fields:

  • User

  • createdAt

  • firstName

They encourage capitalized table names and camelcase fields/columns. However, this flies in the face of the conventions that most database experts would recommend:

If you are not a database expert you have probably never thought about this, and as a Javascript developer you probably still don’t care. That is not my domain as long as I can access my data what does it matter how they are named?

Conventions Matter

You’re a developer. You already know this is important. That is why tools like eslint, prettier, and others exist. Conventions make the parts of writing software that we all seem to enjoy wasting time on automatic.

Conventions also streamline onboarding. When you have an experienced developer joining the team, following industry standards allow that developer to onboard seamlessly into your team. If you have a junior joining having conventions helps them not only learn, but have confidence contributing.

It’s Not All Bad

Okay, but the people over at Prisma are smart, if the default way that they handle naming is SOOOO wrong why do they do it?

Developer Experience over Database Experience

One of benefits of using Prisma as an ORM is how they generate the client you use in your app based off of your schema. This means that their primary focus is on making the developer experience inside of your Typescript/Javascript application is great. This is why they recommend camelCase. It makes the generated code more in line with the conventions of TS/JS code.

Their focus is that the developer experience in your app is great not your experience in the database. They hope you never need to touch the database. However, any app built and used long enough will need to move past the app layer and care about the database, and if you haven’t taken care of how data is managed in your database you are going to have….fun.

The best of both worlds

They know that some people are going to care how their database is managed, they also need to account for people who are migrating existing databases in. So, Prisma left a backdoor when writing your schema to give you control over how your tables and fields are named. This gives you the ability to use conventions on the Prisma side that make working with your database in your application codebase better, while at the same time manually controlling how your schema migrate to the database layer.

Before we jump into what naming conventions you should care about let's talk about how we can manually set them:

How to manually set your Prisma table names

To explicitly set the table name in Prisma you use the @@map attribute. (docs)

model UserPurchase {
  ...
  @@map("user_purchase")
}

How to manually set your Prisma field name

To explicitly set the field names in your Prisma models you will use the @map attribute. (docs)

model Product {
	...
	stripeProduct String @map("stripe_product")
}

Rules for naming things in your database

Okay let's go over three rules when defining your database schema that will save future you and your teammates headaches:

Table names are lowercase and snakecase

There is not a “technical” reason that this has become the standard, but there is a practical one. When you are writing SQL queries the keywords (the actual query syntax) is written using all uppercase words. This means when we use lowercase names for tables there is a very clear separation between the two as well as a common thread when onboarding new developers. They see what they expect when they have to work with the database.

model EmailWorkflow {
  ...
  @@map("email_workflow")
}

Many people still debate whether to use plural or singular table names. In general, it doesn't matter just be consistent in the project. Since you are working with Prisma as your ORM it is easiest in my opinion to just take the singular approach.

Don't name your primary key id

This one is interesting. Prisma is backed by Typescript so when working with a model it doesn't matter if you have 12 models all with the same id field. However, SQL isn't typed. There are no safe guards that you're using the right primary id when you're writing a query. So, make it impossible to mistake the Product primary key and the Price primary key with explicit naming.

Product

❌ id✅ product_id

Price

❌ id✅ price_id

Name foreign keys (relationship ids) explicitly

This one piggybacks on the convention for primary keys, and is one I have been getting wrong since I have been writing database schema.

If you are creating a relationship in Prisma name your foreign keys identical to the primary key of the relationship.

Why? Let's say you are working in a database where you have the concept of a Team and that team is made up of User s. You also have Contacts which is a completely different group of people. Now let's imagine you have a table that holds notes.

model Note {
  id @id @default(cuid()) @map("note_id")

  author Contact @relation(fields: [authorId], references: [id] ...)
  authorId
}

Okay, what's wrong with that? It is clearly notes for contacts.

To Prisma, yes it is. However, in your database you now have a column named authorId and it looks like this:

authorId: cngiwxnsktogke

If you're running a query in a third party tool, do you know what seems like a perfectly good query?

SELECT * FROM notes AS n
LEFT JOIN users as u ON n.owner_id = u.user_id
...

You know what else will be good? The error you'll be looking at and time spent verifying why the foreign keys don't match.

Don't be clever. Be clear.

This is how you can avoid that problem. Make the foreign key match. Do you want to know the best part about working with Prisma? You can still leave the app facing schema field descriptive to your business context. The database doesn't care.

model Note {
  id @id @default(cuid()) @map("note_id")

  author Contact @relation(fields: [contactId], references: [id] ...)
  contactId
}

Now it is immediately obvious you had the query wrong:

SELECT * FROM notes AS n
LEFT JOIN users as u ON n.contact_id = u.user_id
...

I hope you enjoyed

There is a lot more coming...

If you want to get updates when I publish new guides, demos, and more just put your email in below.