Prisma is an open-source next-generation ORM. It consists of the following parts: Prisma Client: Auto-generated and type-safe query builder
https://www.prisma.io
Install prisma/client
npm install @prisma/client
https://www.prisma.io/docs/orm/prisma-client/setup-and-configuration/generating-prisma-client
Generate Prisma Client with the following command:
npx prisma generate
Install prisma
npm install prisma --save-dev
https://www.prisma.io/docs/getting-started/quickstart
set up Prisma with the init command of the Prisma CLI:
npx prisma init --datasource-provider sqlite
to npx prisma init --datasource-provider postgres
Model data in the Prisma schema
prisma/schema.prisma
model Employee {
id String @id @default(cuid())
name String
email String
phone String
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}
Run a migration to create your database tables with Prisma Migrate
npx prisma migrate dev --name init
.env
DATABASE_URL="postgresql://postgres:admin@localhost:5432/postgresDB?schema=public"
https://www.prisma.io/docs/orm/prisma-client/queries/crud
https://nextjs.org/docs/app/building-your-application/routing/route-handlers
app\employee\page.tsx
//app\employee\page.tsx import Link from "next/link"; import TableData from "@/components/tabledata"; import { Suspense } from "react"; import { Spinner } from "@/components/spinner"; import Search from "@/components/search"; const Home = async ({ searchParams, }: { searchParams?: { query?: string; }; }) => { const query = searchParams?.query || ""; return ( <div className="w-screen py-20 flex justify-center flex-col items-center"> <div className="flex items-center justify-between gap-1 mb-5"> <h1 className="text-4xl font-bold">Next.js 14 Search with Prisma PostgreSQL | TailwindCSS DaisyUI</h1> </div> <div className="overflow-x-auto"> <div className="mb-2 w-full text-right"> <Link href="/employee/create" className="btn btn-primary"> Create </Link> </div> <Search /> <Suspense key={query} fallback={<Spinner />}> <TableData query={query}/> </Suspense> </div> </div> ); }; export default Home;components\tabledata.tsx
//components\tabledata.tsx import { getData } from "@/lib/action"; const TableData = async ({ query, }: { query: string; }) => { const employees = await getData(query); return ( <table className="table table-zebra"> <thead className="text-sm text-gray-700 uppercase bg-gray-50"> <tr> <th className="py-3 px-6">#</th> <th className="py-3 px-6">Name</th> <th className="py-3 px-6">Email</th> <th className="py-3 px-6">Phone Number</th> <th className="py-3 px-6">Created At</th> <th className="py-3 px-6 text-center">Actions</th> </tr> </thead> <tbody> {employees.map((rs, index) => ( <tr key={rs.id} className="bg-white border-b"> <td className="py-3 px-6">{index + 1}</td> <td className="py-3 px-6">{rs.name}</td> <td className="py-3 px-6">{rs.email}</td> <td className="py-3 px-6">{rs.phone}</td> <td className="py-3 px-6"> July 11, 2023 </td> <td className="flex justify-center gap-1 py-3"> <button className="btn btn-info">View</button> <button className="btn btn-success">Edit</button> <button className="btn btn-warning">Delete</button> </td> </tr> ))} </tbody> </table> ); }; export default TableData;components\search.tsx
//components\search.tsx "use client"; import { useSearchParams, usePathname, useRouter } from "next/navigation"; import { useDebouncedCallback } from "use-debounce"; //npm i use-debounce https://www.npmjs.com/package/use-debounce const Search = () => { const searchParams = useSearchParams(); const pathname = usePathname(); const { replace } = useRouter(); const handleSearch = useDebouncedCallback((term: string) => { console.log(term); const params = new URLSearchParams(searchParams); if (term) { params.set("query", term); } else { params.delete("query"); } replace(`${pathname}?${params.toString()}`); }, 300); return ( <div className="relative flex flex-1 mb-5 ml-5 mr-5"> <input type="text" className="input input-bordered input-accent w-full" placeholder="Search..." onChange={(e) => handleSearch(e.target.value)} defaultValue={searchParams.get("query")?.toString()} /> </div> ); }; export default Search;components\spinner.tsx
//components\spinner.tsx export const Spinner = () => { return ( <span className="loading loading-spinner loading-lg"></span> ); };lib\action.ts
//lib\action.ts "use server"; import { prisma } from "@/lib/prisma"; export const getData = async (query: string) => { try { const employees = await prisma.employee.findMany({ where: { OR: [ { name: { contains: query, mode: "insensitive", }, }, { phone: { contains: query, mode: "insensitive", }, }, ], }, }); return employees; } catch (error) { throw new Error("Failed to fetch data"); } };lib\action.ts
// import { PrismaClient } from "@prisma/client"; declare global { var prisma: PrismaClient | undefined; } export const prisma = globalThis.prisma || new PrismaClient(); if (process.env.NODE_ENV !== "production") globalThis.prisma = prisma;prisma\schema.prisma
// This is your Prisma schema file, // learn more about it in the docs: https://pris.ly/d/prisma-schema // Looking for ways to speed up your queries, or scale easily with your serverless or edge functions? // Try Prisma Accelerate: https://pris.ly/cli/accelerate-init generator client { provider = "prisma-client-js" } datasource db { provider = "postgresql" url = env("DATABASE_URL") } model Employee { id String @id @default(cuid()) name String email String phone String createdAt DateTime @default(now()) updatedAt DateTime @updatedAt }.env DATABASE_URL="postgresql://postgres:admin@localhost:5432/postgresDB?schema=public"
run C:\nextjs>npm run dev