In this article, you will learn how to add Prisma to a Next.js project. You will learn how to set up Prisma and connect it to a MySQL database. Let's get started!
What is Prisma?
Prisma is an open-source database toolkit that makes it easy to query data from a database inside a TypeScript application. For example, you can use Prisma to query data from a MySQL database inside a Next.js application. Instead of writing plain SQL like this:
SELECT * FROM User WHERE id = 1
You can write the same query using Prisma's query builder like this:
const user = await prisma.user.findUnique({
where: {
id: 1,
},
});
And did I mention that Prisma is TypeScript-first? That means that you get full type-safety and auto-completion in your editor when writing queries. It's cool!
Prerequisites
This tutorial assumes that you have the following tools installed:
And a MySQL database. If you don't have a MySQL database, you can use PlanetScale to get a free database.
Step 1: Create a Next.js project
First, let's create a Next.js project. I will use the Next.js Starter for this tutorial. You can use any Next.js project.
git clone https://github.com/Posandu/nextjs-starter.git nextjs-prisma && cd nextjs-prisma
You now have a Next.js project with TypeScript and ESLint set up. Let's add Prisma to this project.
Step 2: Add Prisma to the project
Next, let's add Prisma to the project. First, install the Prisma CLI:
pnpm install prisma --save-dev
We need to initialize Prisma in the project. Run the following command:
pnpx prisma init
This will create a prisma
directory in the project. The prisma
directory contains a schema.prisma
file. This file contains the database schema. You can edit this file to change the database schema.
Also, it will create a .env
file in the root of the project. This file contains the database connection URL. You can edit this file to change the database connection URL. In PlanetScale, you can find the database connection URL after creating a database.
Click connect in the dashboard to get the connection URL.
In the modal, select the Prisma
option and copy the connection URL.
Now, open the .env
file and paste the connection URL in the .env
file.
DATABASE_URL='mysql://************:**********************@us-east.connect.psdb.cloud/mini-shop?sslaccept=strict'
Now, you can connect to the database using Prisma.
Step 3: Create a database schema
The schema.prisma
file contains the database schema. You can edit this file to change the database schema. For our mini shop, we need a Product
model. Let's add a Product
model to the schema.prisma
file.
model Product {
id Int @id @default(autoincrement())
name String
price Int
img String
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}
The SQL equivalent of this schema is:
CREATE TABLE "Product" (
"id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
"name" TEXT NOT NULL,
"price" INTEGER NOT NULL,
"img" TEXT NOT NULL,
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" DATETIME NOT NULL
);
Now you won't regret using Prisma 😅.
And oh, you need to change
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}
to
datasource db {
provider = "mysql"
url = env("DATABASE_URL")
}
Because we are using MySQL.
Step 4: Push the database schema
Now that we have a database schema, let's push it to the database. Run the following command:
pnpx prisma db push
It will push the database schema.
Step 5: Install Prisma Client
Now that we have a database schema, let's install Prisma Client. We need Prisma Client to query the database. Run the following command:
pnpm install @prisma/client
And generate the types for Prisma Client:
pnpx prisma generate
Step 6: Add Prisma to the Next.js project
Create a folder called lib
and create a file called prisma.ts
in the lib
folder. This file will contain the Prisma Client instance. Add the following code to the prisma.ts
file:
import { PrismaClient } from "@prisma/client";
// PrismaClient is attached to the `global` object in development to prevent
// exhausting your database connection limit.
//
// Learn more:
// https://pris.ly/d/help/next-js-best-practices
let prisma: PrismaClient;
if (process.env.NODE_ENV === "production") {
prisma = new PrismaClient();
} else {
if (!global.prisma) {
global.prisma = new PrismaClient();
}
prisma = global.prisma;
}
export default prisma;
This code will create a Prisma Client instance and export it.
For including the prisma client in the project easily, we will add a typescript path alias. Add the following code to the tsconfig.json
file:
{
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@/*": ["components/*"],
"$/*": ["utils/*"],
"db": ["lib/prisma"]
}
}
}
Now, we can import the Prisma Client instance using import prisma from "db"
.
Query the database
Run the following command to start the development server:
pnpm dev
We will create the API routes for creating a product and getting all the products. Add the following codes to the pages/api
folder:
pages/api/createProduct.ts
This API route will create a product in the database.
import prisma from "db";
import type { NextApiRequest, NextApiResponse } from "next";
type Data = {
success: boolean;
info?: any;
};
export default async function handler(
req: NextApiRequest,
res: NextApiResponse<Data>
) {
let { name, img, price } = req.body;
if (!name || !img || !price) {
res.status(200).json({ success: false });
}
price = parseInt(price);
await prisma.product.create({
data: {
name,
img,
price,
},
});
res.status(200).json({ success: true });
}
pages/api/getProducts.ts
import prisma from "db";
import type { NextApiRequest, NextApiResponse } from "next";
type Data = {
success: boolean;
data?: any;
};
export default async function handler(
req: NextApiRequest,
res: NextApiResponse<Data>
) {
try {
const products = await prisma.product.findMany();
res.status(200).json({ success: true, data: products });
} catch (error) {
res.status(200).json({ success: false });
}
}
This API route will get all the products from the database.
Now let's create a page to create a product and get all the products. Add the following code to the pages
folder:
pages/index.tsx
import {
Box,
Button,
Container,
Flex,
Grid,
Heading,
Text,
} from "@chakra-ui/react";
import type { NextPage } from "next";
import Link from "next/link";
import { useEffect, useState } from "react";
import ColormodeToggle from "@/colormodeToggle";
const Home: NextPage = () => {
const [items, setItems] = useState([]);
useEffect(() => {
fetch("/api/getProducts")
.then((res) => res.json())
.then(({ data }) => setItems(data));
}, []);
return (
<Container maxW="4xl" p={4}>
<Grid templateColumns="repeat(3, 1fr)" gap={4} mb={4}>
<Heading>Home</Heading>
<Link href="/add">
<Button colorScheme="teal" variant="outline">
Add product
</Button>
</Link>
<Flex justifyContent="flex-end">
<ColormodeToggle />
</Flex>
</Grid>
<Grid templateColumns="repeat(3, 1fr)" gap={4}>
{items &&
items.map((item: any) => (
<Box key={item.id} p={4} shadow="md" borderWidth="1px">
{/* eslint-disable-next-line @next/next/no-img-element */}
<img src={item.img} alt={item.name} />
<Text fontSize="xl">{item.name}</Text>
<Text fontSize="lg">{item.price}</Text>
<Button colorScheme="teal" variant="outline">
Add to cart
</Button>
</Box>
))}
{!items.length && (
<Box p={4} shadow="md" borderWidth="1px">
<Text fontSize="xl">No items found</Text>
</Box>
)}
</Grid>
</Container>
);
};
export default Home;
This page will show all the products in the database. We will also add a button to create a product.
pages/add.tsx
import {
Box,
Button,
Container,
Flex,
Grid,
Heading,
Input,
Text,
} from "@chakra-ui/react";
import type { NextPage } from "next";
import Link from "next/link";
import { useState } from "react";
import ColormodeToggle from "@/colormodeToggle";
const Home: NextPage = () => {
const [data, setData] = useState({
name: "",
price: "",
img: "",
});
const add = () => {
fetch("/api/createProduct", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(data),
})
.then((res) => res.json())
.then(({ data }) => console.log(data));
};
return (
<Container maxW="4xl" p={4}>
<Grid templateColumns="repeat(3, 1fr)" gap={4} mb={4}>
<Heading>Add product</Heading>
<Link href="/">
<Button colorScheme="teal" variant="outline">
Return to home
</Button>
</Link>
<Flex justifyContent="flex-end">
<ColormodeToggle />
</Flex>
</Grid>
{["name", "price", "img"].map((item) => (
<Box key={item}>
<Text fontSize="xl">
{item.replace(/^\w/, (c) => c.toUpperCase())}
</Text>
<Input
placeholder={item}
value={
// @ts-ignore
data[item]
}
onChange={(e) => setData({ ...data, [item]: e.target.value })}
mb={4}
/>
</Box>
))}
<Button colorScheme="teal" onClick={add}>
Add
</Button>
</Container>
);
};
export default Home;
This page will allow us to create a product. We will add a button to return to the home page.
Now, add some products to the database from the /add
page.
After adding some products, you should see something like this:
It works! Now, you can improve some stuff like adding a cart page, adding a delete button, etc.
Conclusion
You can find the complete code on GitHub. And if you have any questions, feel free to ask them in the comments or on Twitter.
If you liked this tutorial, please consider supporting me on Buy Me a Coffee.
Thanks for reading!
Top comments (0)