DEV Community

Aryan J
Aryan J

Posted on • Edited on

Introduction to Prisma 2 with Next.js

Introduction to Prisma with Next.js

This tutorial outlines how to use Prisma along with Next.js. It assumes a basic understanding of Next.js (which, in turn, assumes basic knowledge of React). By the end of the tutorial, you will have stood up a Prisma client, a development SQLite database, and a Next.js application.

This tutorial will not cover GraphQL with Prisma. If that is something you're interested in, please refer to my live coding video where I add Prisma with GraphQL to a Next.js project.

Create a New Next.js Project

From your terminal, run:

npm init next-app
# or
yarn create next-app

You can find more information about the commands above in the Next.js docs.

This will install a new Next.js project with the directory name that you specified. Run:

npm run dev
# or
yarn dev  

to start the development server. Now visit http://localhost:3000 to see your Next.js app. Edit /pages/index. js to modify this page.

Install Prisma

From your terminal, run:

npm install @prisma/client
npm install @prisma/cli --save-dev
# or
yarn add @prisma/client
yarn add @prisma/cli -D

This installs the Prisma CLI which we can now use to initialize Prisma.

Initialize Prisma

From your terminal, run

npx prisma init
# or
yarn prisma init

This creates two new files: /prisma/.env and prisma/schema.prisma.

  • /prisma/.env - Defines the environment variables used in /schema.prisma. Learn more here.
  • /schema.prisma - datasource, generator and model definitions are added here. Prisma will use this to generate migrations, GraphQL schemas and TypeScript types. Learn more here.

Add SQLite Database as Datasource

For the purposes of this tutorial, we will use a SQLite database. In /prisma/.env, change DATABASE_URL to file:./dev.db:

# /prisma/.env
DATABASE_URL="file:./dev.db"

and in /prisma/schema.prisma change the datasource provider to sqlite :

# prisma/schema.prisma
datasource db {
  provider = "sqlite"
  url      = env("DATABASE_URL")
}
# ... the rest of the file

If you're interested in using postgres as your database, you can skip this step and, instead, replace DATABASE_URL with your actual postgres URL.

Add a Data Model

For simplicity's sake, we will create a lightweight data model. In /prisma/schema.prisma, add the movie model:

# prisma/schema.prisma
# ...stuff at the top of the file
model Movie {
  id           Int    @default(autoincrement()) @id
  director     String
  movieName    String
  yearReleased Int
}

Generate and Run Database Migrations

With the our model all set up, we're ready to generate migrations that will add these tables to our database. First, run:

npx prisma migrate save --experimental
# or
yarn prisma migrate save --experimental

Since we haven't done so, it will create a SQLite database for you. It will ask for a migration name; this is optional. You can view the newly created migration in the /prisma/migrations folder. Take a look and you'll see that it generated all the SQL necessary to create the new movies table.

Note that the new table wasn't created; simply the code to create said table. To create the table, this migration needs to be run. We do this by running:

npx prisma migrate up --experimental
# or
yarn prisma migrate up --experimental

This command will run any migrations that have not yet been run. After this, you can use your favorite database client (I'm partial to TablePlus) to view your database.

Learn more about Prisma migrations here.

Generate the Prisma Client

The Prisma Client is a database client tailored to your database schema. This means that we will easily be able to call a function that runs CRUD operations against our database. In our case, this means the Prisma Client will have a movies layer with functions to perform all CRUD operations.

To generate the Prisma Client, run:

npx prisma generate
# or
yarn prisma generate

Our Prisma Client is ready for use.

Learn more about the Prisma Client here.

Add a POST endpoint to Create New Movies

Let's use our newly created Prisma Client to create a /movies/ endpoint that creates new movies. In /pages/api/movies.js (you'll have to create an /api directory and a movies.js file), paste the following:

// /pages/api/movies.js
import { PrismaClient } from '@prisma/client';

const prisma = new PrismaClient();

export default async function (req, res) {
  if (req.method === 'POST') {
    const { body } = req;
    const movie = await prisma.movie.create({ data: JSON.parse(body) });
    res.json(movie);
  }
}

Any file inside the folder /pages/api is mapped to /api/* and treated as an API endpoint. In our case, we now have an endpoint at http://localhost:3000/api/movies.

Stepping down the file, we:

  1. Import the PrismaClient from @prisma/client. Yes, our own personal database client is saved to node_modules. The generated code is saved to node_modules and is referred to as a "smart node module." You can learn more here.
  2. Initialize an instance of the PrismaClient and save it to a variable called prisma.
  3. Use a Next.js API Route and only handle POST request.
  4. Use the body of the request and the Prisma client to create a new movie and save it to a variable called movie. Note that we parse the body since we will send it as a JSON string.
  5. Respond with the movie created in the step above.

Create a Movie Submission Form

We will use Formik to create our movie submission form. Please feel free to not use Formik if you're more comfortable using HTML forms (or any other React forms library).

If you're using Formik, run:

npm install formik
# or
yarn add formik

Replace all of pages/index.js with:

// /pages/index.js
import { Field, Formik, Form } from 'formik';

const Home = () => (
  <div className="container">
    <Formik
      initialValues={{
        director: '',
        movieName: '',
        yearReleased: '',
      }}
      onSubmit={(values) => {
        fetch('http://localhost:3000/api/movies', {
          method: 'POST',
          body: JSON.stringify({ ...values, yearReleased: Number(values.yearReleased) }),
        });
      }}
    >
      <Form>
        <label>
          Movie Name
          <Field name="movieName" type="text"></Field>
        </label>
        <label>
          Director
          <Field name="director" type="text"></Field>
        </label>
        <label>
          Year Released
          <Field name="yearReleased" type="text"></Field>
        </label>
        <button type="submit">Submit</button>
      </Form>
    </Formik>
  </div>
);

export default Home;

The code above should be straightforward. The submit function makes a POST request to our /api/movies endpoint. Please note that we need to cast yearReleased here to a number as the Prisma client expects an Int (as defined in /prisma/schema.prisma.

Now, create a movie and press submit. Using your database client, you can view the movies table and see that a new movie was created.

Use Server-Side Rendering to Hydrate Page Props on the Server

This right here is where the beauty of Next.js comes in. We can use getServerSideProps to fetch our movie data on the server-side (using our Prisma Client). The movies will be pre-rendered on our homepage (no client-side fetching necessary!).

Normally, you might create a GET endpoint that uses the Prisma client to fetch a list of movies and respond with said list of movies. With getServerSideProps, there's no need for the endpoint. We can use the Prisma client directly in that function to get the list of movies and inject our page's props with the movies.

At the bottom of our /pages/index.js file, add the following code:

// /pages/index.js

// Home component cut for brevity

export const getServerSideProps = async () => {
  const prisma = new PrismaClient();
  const movies = await prisma.movie.findMany();
  return { props: { movies } };
};

Here, we:

  1. Instantiate our PrismaClient
  2. Get all movies using the prisma instance
  3. Return a props object that includes the movies we just fetched

With this code, our Home component now has movies props. For example, we can render the movies as follows:

// /pages/index.js
const Home = ({ movies }) => (
  <div className="container">
    {movies.map((movie) => (
      <div key={movie.id}>
        <p>Name: {movie.movieName}</p>
        <p>Director: {movie.director}</p>
        <p>Year Released: {movie.yearReleased}</p>
      </div>
    ))}
    <Formik>
      {/* your movie form is here; cut for brevity */}
    </Formik>
  </div>
);

Again, there is NO fetching of movies on the client. It's all done on the server.

Mission Accomplished

This tutorial should be enough to get you started with Next.js and Prisma.

If you have any questions or feedback, you can reach me on Twitter: @aryanjabbari.

Top comments (6)

Collapse
 
bac1 profile image
Bac

How to use pg master/slave with prisma?

Collapse
 
toilahungvn98 profile image
Nguyễn Việt Hưng

how to upload file with prisma ?

Collapse
 
vhflat profile image
Vegard

What if you want to use prisma with multiple endpoints? Do you generate a client for each endpoint, or do you export it and import it in every endpoint?

Collapse
 
aryanjnyc profile image
Aryan J

I'd just generate a Prisma client for each endpoint. If you're using some type of serverless endpoint, I'd generate the Prisma client outside of the called function so only one client is generated (using node's caching).

Collapse
 
noahvlncrt profile image
Noah Vaillancourt

I know it's been an eternity since you posted this but it really saved me today. Thank you

Collapse
 
aryanjnyc profile image
Aryan J

I love hearing it, Noah! :)