Getting Started (Quickstart)

Get started with Prisma Util by following a step-by-step tutorial that guides you through the most important features and offers a starting point for learning the concepts for this tool.

Prerequisites

For this Getting Started guide, you'll only need a few things:

  • A Node.js Project, running Node.js v14.17.0 or higher and NPX (If you don't know what NPX is, it's basically a runtime for CLI tools, you can read more about it here)

  • A Prisma Setup, configured with a database using an environment file (You can follow the quick-start guide on Prisma's official website by going to this link)

  • Access to a Terminal and an Internet connection (this connection is required to download the packages from NPM)

Background Knowledge

This Getting Started guide assumes that you are a new user to Prisma Util and won't go into heavy detail on certain concepts and features. As such, you can follow this tutorial without any background experience with this tool. If you want a more advanced guide, check out our Advanced Getting Started guide by clicking here.

This guide supports Interactive Mode

You can run this guide on your machine by using the Interactive CLI. This CLI will provide a way for you to follow step-by-step instructions in your terminal, to remove the friction of going from editor to browser and back again. To start this guide in Interactive Mode, run these commands:

Terminal
npm i prisma-util
npx prisma-util interactive --tutorial getting-started

1. Initialize the configuration file

As a first step, navigate to your project directory and run the following command. These commands will install and initialize the prisma-util.config.mjs file, which will allow you to configure the behavior of this tool.

Terminal
npm i prisma-util
npx prisma-util prepare

2. Configuring Prisma Util

During this guide, we'll use the following schema as a reference. You can follow the tutorial with your own schema or use this one that we have created. This schema is made to showcase the most common and simple workflows with Prisma Util and to demonstrate the power of this library. At the end of the guide, this initial schema will be broken down into multiple files and Prisma Util will be configured accordingly to be able to process the new data.

prisma/schema.prisma
generator client {
  provider = "prisma-client-js"
  engineType = "binary"
}

datasource db {
  provider = "postgresql"
  url = env("DATABASE_URL")
}

model User {
  id Int @id @default(autoincrement())
  email String @unique
  name String?
  posts Post[]
}

model Post {
  id Int @id @default(autoincrement())
  title String
  content String?
  published Boolean @default(false)
  author User @relation(fields: [authorId], references: [id])
  authorId Int
}

We are also going to use this file structure:

File Structure
quickstart (Project Root)
    @node_modules
    prisma
        schema.prisma
    .env
    package.json
    prisma-util
        types
        config.mjs

2.0. The initial Prisma Util configuration

Let's take a look at the initial configuration file that we've generated using npx prisma-util prepare. The file that Prisma Util created looks something like this:

prisma-util/config.mjs
/**
* @typedef {string | (model?: string, name?: string) => string} FileModelConfig
* @typedef {"crossFileRelations"} OptionalFeatures
*/

/**
* @typedef {Object} Configuration
* 
* @property {FileModelConfig} baseSchema 
* The file that contains your generator and datasource. This path is relative to your project root.
* To find out more about configuring the base schema, read {@link https://prisma-util.gitbook.io/prisma-util/api-documentation#base-schema this} documentation section.
* 
* @property {FileModelConfig[]} includeFiles
* Files in this array will be merged in to the final schema by Prisma Util. 
* To find out more about configuring the included files, read {@link https://prisma-util.gitbook.io/prisma-util/api-documentation#include-files this} documentation section.
* 
* @property {string[]?} excludeModels
* This array uses the `file:model` association defined in the Prisma Util concepts. Models in this array will be excluded from the final build.
* To find out more about configuring the excluded models, read {@link https://prisma-util.gitbook.io/prisma-util/api-documentation#exclude-models this} documentation section.
* 
* @property {OptionalFeatures[]} optionalFeatures
* Allows you to enable optional features to supercharge your Prisma Util setup.
* To find out more about configuring optional features, read {@link https://prisma-util.gitbook.io/prisma-util/api-documentation#optional-features this} documentation section.
* 
* @property {{[fileModel: string]: string}?} extended
* Create model inheritance within Prisma! The model defined by the value of this key-value entry will receive all non-id non-relation fields from the model defined by the key.
* To find out more about configuring model inheritance, read {@link https://prisma-util.gitbook.io/prisma-util/api-documentation#extend-models this} documentation section.
* 
* @property {{[fileModelColumn: string]: string}?} relations
* Indicate a cross-file relation between the column defined by the key of this key-value entry and the model defined by the value.
* To find out more about configuring cross-file relations, read {@link https://prisma-util.gitbook.io/prisma-util/api-documentation#cross-file-relations this} documentation section.
* 
* @property {Promise<string>[]?} codeGenerators
* Code generators allow you to create models at migration-time, to allow dynamic computation.
* To find out more about configuring code generators, read {@link https://prisma-util.gitbook.io/prisma-util/api-documentation#code-generators this} documentation section.
* 
* @property {{[fileModel: string]: {type: "Gin" | "Gist", indexes: { language: string, field: string, weight: string }[]}}?} ftsIndexes
* Create FTS Indexes for your models for a faster experience.
* To find out more about configuring full-text seach indexes, read {@link https://prisma-util.gitbook.io/prisma-util/api-documentation#full-text-search-indexes this} documentation section.
* 
* @property {string} schema
* The schema of this database.
* To find out more about configuring full-text seach indexes, read {@link https://prisma-util.gitbook.io/prisma-util/api-documentation#schema this} documentation section.
* 
* @property {string} middleware
* The path where the middleware should be created.
* To find out more about configuring full-text seach indexes, read {@link https://prisma-util.gitbook.io/prisma-util/api-documentation#middleware this} documentation section.
*/

/**
 * @type {Configuration}
 */
export default {
    optionalFeatures: [],
    includeFiles: [],
    baseSchema: '',
};

I know that it might seem threatening because of the massive JSDocs, but those are added to improve your developer experience and should be treated for what they are: comments. If we overlook the documentation that Prisma Util has created for us, we can actually see a pretty barebones setup: just a simple object that is the default export of this file with 3 keys. The Prisma Util configuration file is an interactive configuration experience: You can import Node.js modules, run functions and in the end, return a configuration object that the tool can understand. The Prisma Util configuration works in a very intuitive way: You can do whatever you want in the file, as long as you export a default object that follows the type provided. We'll explore this in the next sections, because we are going to configure the way that Prisma Util works to tailor it according to our project. (and we'll enable some cool optional features as a bonus)

2.1. Creating the base schema for Prisma Util

Our current schema is pretty massive, so let's start breaking it down. Prisma Util requires a base schema with two blocks: the generator and the datasource. Let's create a file called base.prisma inside of the prisma folder that will hold our generator and datasource:

prisma/base.prisma
generator client {
  provider = "prisma-client-js"
  engineType = "binary"
}

datasource db {
  provider = "postgresql"
  url = env("DATABASE_URL")
}

There you go, now it's looking much better. Let's edit the configuration file for Prisma Util to let it know that it should look in this new file for the generator and datasource. This is very simple to do, we just need to edit the baseSchema string and bring our configuration file to this state:

prisma-util/config.mjs
// The JSDocs are excluded from this code block to make 
// it more concise and easy to follow.
export default {
    optionalFeatures: [],
    includeFiles: [],
    baseSchema: 'prisma/base.prisma',
};

2.2. Splitting the models in different files

Right now, we have two models (Post and User) in the same file. This might work right now, but we want to make this system scalable. What big projects do is that they split models into different files based on scope. In this case, we can have a prisma/post.prisma file for our Post model and a prisma/user.prisma file for our User model. So, let's split the 2 files as follows:

prisma/user.prisma
model User {
  id Int @id @default(autoincrement())
  email String @unique
  name String?
  posts Post[]
}
prisma/post.prisma
model Post {
  id Int @id @default(autoincrement())
  title String
  content String?
  published Boolean @default(false)
  author User @relation(fields: [authorId], references: [id])
  authorId Int
}

With this split, we've emptied the prisma/schema.prisma file. We can get rid of this file, because we no longer need it. If you've followed all of the steps up until now, the file structure should look like this:

File Structure
quickstart (Project Root)
    @node_modules
    prisma
        base.prisma
        user.prisma
        post.prisma
    .env
    package.json
    prisma-util
        types
        config.mjs

Now it's time for us to go back to the Prisma Util configuration file. Remember that pretty includeFiles array? We're going to use it to let Prisma Util know that it should include the user.prisma and post.prisma files in our final bundle. To do this, we're going to use the help of the schema-creator utility module. This module is built-in, and it will handle the complicated File-Model-Column associations for us so we don't have to worry about inconsistencies. (If you are interested about this topic, you can learn more about our recommendations for path declarations here)

Let's use the Schema Creator module it in our configuration file to create our model definitions. To do this, we just need to edit the configuration file as follows:

prisma-util/config.mjs
// The JSDocs are excluded from this code block to make 
// it more concise and easy to follow.

// This line will import the function that we need
import { constantModel } from "prisma-util/schema-creator";

// Let's create a model definition for the Post model and the User model
// We just need to pass the path to the .prisma file to constantModel
const PostDefinition = constantModel("prisma/post.prisma");
const UserDefinition = constantModel("prisma/user.prisma");

export default {
    optionalFeatures: [],
    // Now we can use the model definitions created earlier to include the files
    includeFiles: [PostDefinition, UserDefinition],
    baseSchema: "prisma/base.prisma"
}

3. Fixing relation problems

Remember how our 2 models had a relation initially? We need to do just a bit more configuration to allow those relations to function adequately. To do this, we'll start by removing the posts[] field from our User model, because it will be automatically added by Prisma Util:

prisma/user.prisma
model User {
  id Int @id @default(autoincrement())
  email String @unique
  name String?
}

If you are using the Prisma extension for VSCode, this will fix the "missing model" issue. Now it's time to fix the relation from the Post model. To do this, we'll create a "fake" User model so we can satisfy the Prisma extension for VSCode in a way that won't show any issues. To do this, let's edit the prisma/post.prisma file like this:

prisma/post.prisma
model User {
  id Int @id @default(autoincrement())
  posts Post[]
}

model Post {
  id Int @id @default(autoincrement())
  title String
  content String?
  published Boolean @default(false)
  author User @relation(fields: [authorId], references: [id])
  authorId Int
}

Notice how the Prisma extension for VSCode doesn't complain about the missing model anymore? However, you might be wondering: Doesn't this lead to a naming conflict? Now we have 2 User models! The answer is usually yes, but we'll do a clever trick to fix that.

This trick uses an optional feature called Cross-File Relations. Think about it as a mapper that replaces the "fake" User model we just created with the true original model.

To use this optional feature, we have to enable it in the configuration file like this:

prisma-util/config.mjs
// The JSDocs are excluded from this code block to make 
// it more concise and easy to follow.
import { constantModel } from "prisma-util/schema-creator";

const PostDefinition = constantModel("prisma/post.prisma");
const UserDefinition = constantModel("prisma/user.prisma");

export default {
    // We need to add the "crossFileRelations" optional feature to this array.
    optionalFeatures: ["crossFileRelations"],
    includeFiles: [PostDefinition, UserDefinition],
    baseSchema: "prisma/base.prisma"
}

We're not done yet however. We need to tell the mapper how to parse our relations. To do this, we'll use a new field called relations, as well as the definitions that we've just created using the Schema Creator module.

With this in mind, let's edit the configuration file one last time:

prisma-util/config.mjs
// The JSDocs are excluded from this code block to make 
// it more concise and easy to follow.
import { constantModel } from "prisma-util/schema-creator";

const PostDefinition = constantModel("prisma/post.prisma");
const UserDefinition = constantModel("prisma/user.prisma");

export default {
    optionalFeatures: ["crossFileRelations"],
    includeFiles: [PostDefinition, UserDefinition],
    baseSchema: "prisma/base.prisma",
    // This is our new field!
    relations: {
        // The key for this object will be the field that we want to remap
        // In our case, it is the "author" field of the Post model

        // The value for this object is the new type for the field
        // In our case, it is the User model
        [PostDefinition("Post", "author")]: UserDefinition("User")
    }
}

We're done! Now we are ready to run Prisma commands.

4. Running Prisma commands

Prisma Util is a wrapper around the Prisma CLI. Because of this, the commands are exactly the same. Let's migrate the database and generate the PrismaClient that we'll use in our code:

Terminal
npx prisma-util migrate dev

That's it!

Wrapping Up

Congratulations on finishing the Prisma Util Quickstart guide! We hope that you've enjoyed the learning experience. If you want to proceed with more advanced concepts, you can follow the guides in this section or read about the concepts here.

Last updated