diff --git a/.github/workflows/issue_stale.yml b/.github/workflows/issue_stale.yml index f46757073993e6..ce09df573b1859 100644 --- a/.github/workflows/issue_stale.yml +++ b/.github/workflows/issue_stale.yml @@ -23,8 +23,8 @@ jobs: remove-issue-stale-when-updated: true stale-issue-label: 'stale' labels-to-add-when-unstale: 'not stale' - stale-issue-message: 'This issue has been automatically marked as stale due to two years of inactivity. It will be closed in 7 days unless there’s further input. If you believe this issue is still relevant, please leave a comment or provide updated details. Thank you.' - close-issue-message: 'This issue has been automatically closed due to two years of inactivity. If you’re still experiencing a similar problem or have additional details to share, please open a new issue following our current issue template. Your updated report helps us investigate and address concerns more efficiently. Thank you for your understanding!' + stale-issue-message: 'This issue has been automatically marked as stale due to inactivity. It will be closed in 7 days unless there’s further input. If you believe this issue is still relevant, please leave a comment or provide updated details. Thank you.' + close-issue-message: 'This issue has been automatically closed due to inactivity. If you’re still experiencing a similar problem or have additional details to share, please open a new issue following our current issue template. Your updated report helps us investigate and address concerns more efficiently. Thank you for your understanding!' operations-per-run: 300 # 1 operation per 100 issues, the rest is to label/comment/close - uses: actions/stale@v9 id: stale-no-repro diff --git a/examples/prisma-postgres/.gitignore b/examples/prisma-postgres/.gitignore new file mode 100644 index 00000000000000..c2f6b88b346d30 --- /dev/null +++ b/examples/prisma-postgres/.gitignore @@ -0,0 +1,44 @@ +# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. + +# dependencies +/node_modules +/.pnp +.pnp.* +.yarn/* +!.yarn/patches +!.yarn/plugins +!.yarn/releases +!.yarn/versions + +# testing +/coverage + +# next.js +/.next/ +/out/ + +# production +/build + +# misc +.DS_Store +*.pem + +# debug +npm-debug.log* +yarn-debug.log* +yarn-error.log* +.pnpm-debug.log* + +# env files (can opt-in for committing if needed) +.env* + +# vercel +.vercel + +# typescript +*.tsbuildinfo +next-env.d.ts + +# generated Prisma Client +/lib/generated/prisma-client \ No newline at end of file diff --git a/examples/prisma-postgres/README.md b/examples/prisma-postgres/README.md new file mode 100644 index 00000000000000..d0b491ce94fd1e --- /dev/null +++ b/examples/prisma-postgres/README.md @@ -0,0 +1,259 @@ +# Prisma ORM + Next.js starter + +This repository provides boilerplate to quickly set up a simple Next.js CRUD application with [Prisma Postgres](https://www.prisma.io/postgres?utm_source=nextjs&utm_medium=example&utm_campaign=nextjs_example) and [Prisma ORM](https://www.prisma.io/orm?utm_source=nextjs&utm_medium=example&utm_campaign=nextjs_example) for database operations. + +## Getting started + +Follow these steps to quickly set up the project and start using Prisma ORM with Next.js. + +### 1. Create a Next.js app + +Run [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app) using your preferred package manager: + +```bash +# Using npm +npx create-next-app@latest --example prisma-postgres my-prisma-postgres-app +``` + +
+ +Expand for yarn, pnpm or bun + +```bash +# Using yarn +yarn create next-app --example prisma-postgres my-prisma-postgres-app + +# Using pnpm +pnpm create-next-app --example prisma-postgres my-prisma-postgres-app + +# Using bun +bunx create-next-app --example prisma-postgres my-prisma-postgres-app +``` + +
+ +Navigate into the created app: + +```bash +cd ./my-prisma-postgres-app +``` + +Install the dependencies if you haven't already: + +```bash +# Using npm +npm install +``` + +
+ +Expand for yarn, pnpm or bun + +```bash +# Using yarn +yarn install + +# Using pnpm +pnpm install + +# Using bun +bun install +``` + +
+ +### 2. Create a Prisma Postgres instance + +Run the following command in your terminal: + +``` +npx prisma init --db +``` + +If you don't have a [Prisma Data Platform](https://console.prisma.io/) account yet, or if you are not logged in, the command will prompt you to log in using one of the available authentication providers. A browser window will open so you can log in or create an account. Return to the CLI after you have completed this step. + +Once logged in (or if you were already logged in), the CLI will prompt you to: + +1. Select a **region** (e.g. `us-east-1`) +1. Enter a **project name** + +After successful creation, you will see output similar to the following: + +
+ +CLI output + +```terminal +Let's set up your Prisma Postgres database! +? Select your region: ap-northeast-1 - Asia Pacific (Tokyo) +? Enter a project name: testing-migration +✔ Success! Your Prisma Postgres database is ready ✅ + +We found an existing schema.prisma file in your current project directory. + +--- Database URL --- + +Connect Prisma ORM to your Prisma Postgres database with this URL: + +prisma+postgres://accelerate.prisma-data.net/?api_key=ey... + +--- Next steps --- + +Go to https://pris.ly/ppg-init for detailed instructions. + +1. Install and use the Prisma Accelerate extension +Prisma Postgres requires the Prisma Accelerate extension for querying. If you haven't already installed it, install it in your project: +npm install @prisma/extension-accelerate + +...and add it to your Prisma Client instance: +import { withAccelerate } from "@prisma/extension-accelerate" + +const prisma = new PrismaClient().$extends(withAccelerate()) + +2. Apply migrations +Run the following command to create and apply a migration: +npx prisma migrate dev + +3. Manage your data +View and edit your data locally by running this command: +npx prisma studio + +...or online in Console: +https://console.prisma.io/{workspaceId}/{projectId}/studio + +4. Send queries from your app +If you already have an existing app with Prisma ORM, you can now run it and it will send queries against your newly created Prisma Postgres instance. + +5. Learn more +For more info, visit the Prisma Postgres docs: https://pris.ly/ppg-docs +``` + +
+ +Locate and copy the database URL provided in the CLI output. Then, follow the instructions in the next step to create a `.env` file in the project root. + +### 3. Setup your `.env` file + +You now need to configure your database connection via an environment variable. + +First, create an `.env` file: + +```bash +touch .env +``` + +Then update the `.env` file by replacing the existing `DATABASE_URL` value with the one you previously copied. It will look similar to this: + +```bash +DATABASE_URL="prisma+postgres://accelerate.prisma-data.net/?api_key=PRISMA_POSTGRES_API_KEY" +``` + +### 4. Migrate the database + +Run the following commands to set up your database and Prisma schema: + +```bash +# Using npm +npx prisma migrate dev --name init +``` + +
+ +Expand for yarn, pnpm or bun + +```bash +# Using yarn +yarn prisma migrate dev --name init + +# Using pnpm +pnpm prisma migrate dev --name init + +# Using bun +bun prisma migrate dev --name init +``` + +
+ +### 5. Seed the database + +Add initial data to your database: + +```bash +# Using npm +npx prisma db seed +``` + +
+ +Expand for yarn, pnpm or bun + +```bash +# Using yarn +yarn prisma db seed + +# Using pnpm +pnpm prisma db seed + +# Using bun +bun prisma db seed +``` + +
+ +### 6. Run the app + +Start the development server: + +```bash +# Using npm +npm run dev +``` + +
+ +Expand for yarn, pnpm or bun + +```bash +# Using yarn +yarn dev + +# Using pnpm +pnpm run dev + +# Using bun +bun run dev +``` + +
+ +Once the server is running, visit `http://localhost:3000` to start using the app. + +## Usage + +The app includes the following routes: + +- `/`: Display the thee most recent posts +- `/posts`: Paginated list view of all posts +- `/posts/new`: Create a new post +- `/users/new`: Create a new user +- `/api/posts/`: Pagination logic + +## Deploy your own + +Deploy the example using [Vercel](https://vercel.com?utm_source=github&utm_medium=readme&utm_campaign=next-example): + +[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new/clone?repository-url=https%3A%2F%2Fgithub.com%2Fvercel%2Fnext.js%2Ftree%2Fcanary%2Fexamples%2Fprisma-orm&env=DATABASE_URL&envDescription=Add%20your%20PRISMA%20POSTGRES%20database%20url&project-name=prisma-orm-app&repository-name=prisma-orm) + +## Additional information + +Explore different ways to use Prisma ORM in your project with the following resources to help you expand your knowledge and customize your workflow: + +- Prisma ORM supports multiple databases. Learn more about the supported databases [here](https://www.prisma.io/docs/orm/reference/supported-databases?utm_source=nextjs&utm_medium=example&utm_campaign=nextjs_example). +- To use Prisma ORM in an edge runtime without using [Prisma Postgres](https://www.prisma.io/docs/orm/overview/databases/prisma-postgres?utm_source=nextjs&utm_medium=example&utm_campaign=nextjs_example) or [Prisma Accelerate](https://www.prisma.io/docs/accelerate/getting-started?utm_source=nextjs&utm_medium=example&utm_campaign=nextjs_example), refer to the [driver adapters guide](https://www.prisma.io/docs/orm/prisma-client/deployment/edge/deploy-to-vercel?utm_source=nextjs&utm_medium=example&utm_campaign=nextjs_example). + +For further learning and support: + +- [Prisma ORM documentation](https://www.prisma.io/docs/orm?utm_source=nextjs&utm_medium=example&utm_campaign=nextjs_example) +- [Prisma Client API reference](https://www.prisma.io/docs/orm/prisma-client?utm_source=nextjs&utm_medium=example&utm_campaign=nextjs_example) +- [Join our Discord community](https://pris.ly/discord?utm_source=nextjs&utm_medium=example&utm_campaign=nextjs_example) +- [Follow us on Twitter](https://pris.ly/x?utm_source=nextjs&utm_medium=example&utm_campaign=nextjs_example) diff --git a/examples/prisma-postgres/app/Header.tsx b/examples/prisma-postgres/app/Header.tsx new file mode 100644 index 00000000000000..66b91ab5174ce9 --- /dev/null +++ b/examples/prisma-postgres/app/Header.tsx @@ -0,0 +1,32 @@ +"use client"; + +import Link from "next/link"; + +export default function Header() { + return ( +
+ +
+ ); +} diff --git a/examples/prisma-postgres/app/api/posts/route.ts b/examples/prisma-postgres/app/api/posts/route.ts new file mode 100644 index 00000000000000..a7af6e60543195 --- /dev/null +++ b/examples/prisma-postgres/app/api/posts/route.ts @@ -0,0 +1,22 @@ +import prisma from "@/lib/prisma"; +import { NextResponse } from "next/server"; + +export async function GET(request: Request) { + const url = new URL(request.url); + const page = parseInt(url.searchParams.get("page") || "1"); + const postsPerPage = 5; + const offset = (page - 1) * postsPerPage; + + // Fetch paginated posts + const posts = await prisma.post.findMany({ + skip: offset, + take: postsPerPage, + orderBy: { createdAt: "desc" }, + include: { author: { select: { name: true } } }, + }); + + const totalPosts = await prisma.post.count(); + const totalPages = Math.ceil(totalPosts / postsPerPage); + + return NextResponse.json({ posts, totalPages }); +} diff --git a/examples/prisma-postgres/app/favicon.ico b/examples/prisma-postgres/app/favicon.ico new file mode 100644 index 00000000000000..718d6fea4835ec Binary files /dev/null and b/examples/prisma-postgres/app/favicon.ico differ diff --git a/examples/prisma-postgres/app/globals.css b/examples/prisma-postgres/app/globals.css new file mode 100644 index 00000000000000..6b717ad346d3df --- /dev/null +++ b/examples/prisma-postgres/app/globals.css @@ -0,0 +1,21 @@ +@tailwind base; +@tailwind components; +@tailwind utilities; + +:root { + --background: #ffffff; + --foreground: #171717; +} + +@media (prefers-color-scheme: dark) { + :root { + --background: #0a0a0a; + --foreground: #ededed; + } +} + +body { + color: var(--foreground); + background: var(--background); + font-family: Arial, Helvetica, sans-serif; +} diff --git a/examples/prisma-postgres/app/layout.tsx b/examples/prisma-postgres/app/layout.tsx new file mode 100644 index 00000000000000..dc81382de38064 --- /dev/null +++ b/examples/prisma-postgres/app/layout.tsx @@ -0,0 +1,25 @@ +// app/layout.tsx +import "./globals.css"; +import Header from "./Header"; + +export const metadata = { + title: "Superblog", + description: "A blog app using Next.js and Prisma", +}; + +export default function RootLayout({ + children, +}: { + children: React.ReactNode; +}) { + return ( + + +
+
+
{children}
+
+ + + ); +} diff --git a/examples/prisma-postgres/app/page.tsx b/examples/prisma-postgres/app/page.tsx new file mode 100644 index 00000000000000..4664eb4864d498 --- /dev/null +++ b/examples/prisma-postgres/app/page.tsx @@ -0,0 +1,55 @@ +export const dynamic = "force-dynamic"; // This disables SSG and ISR + +import prisma from "@/lib/prisma"; +import Link from "next/link"; + +export default async function Home() { + const posts = await prisma.post.findMany({ + orderBy: { + createdAt: "desc", + }, + take: 6, + include: { + author: { + select: { + name: true, + }, + }, + }, + }); + + return ( +
+

+ Recent Posts +

+
+ {posts.map((post) => ( + +
+

+ {post.title} +

+

+ by {post.author ? post.author.name : "Anonymous"} +

+

+ {new Date(post.createdAt).toLocaleDateString("en-US", { + year: "numeric", + month: "long", + day: "numeric", + })} +

+
+

+ {post.content || "No content available."} +

+
+
+
+ + ))} +
+
+ ); +} diff --git a/examples/prisma-postgres/app/posts/[id]/page.tsx b/examples/prisma-postgres/app/posts/[id]/page.tsx new file mode 100644 index 00000000000000..090ebf408f9769 --- /dev/null +++ b/examples/prisma-postgres/app/posts/[id]/page.tsx @@ -0,0 +1,77 @@ +export const dynamic = "force-dynamic"; // This disables SSG and ISR + +import prisma from "@/lib/prisma"; +import { notFound, redirect } from "next/navigation"; + +export default async function Post({ + params, +}: { + params: Promise<{ id: string }>; +}) { + const { id } = await params; + const postId = parseInt(id); + + const post = await prisma.post.findUnique({ + where: { id: postId }, + include: { + author: true, + }, + }); + + if (!post) { + notFound(); + } + + // Server action to delete the post + async function deletePost() { + "use server"; + + await prisma.post.delete({ + where: { + id: postId, + }, + }); + + redirect("/posts"); + } + + return ( +
+
+ {/* Post Title */} +

+ {post.title} +

+ + {/* Author Information */} +

+ by{" "} + + {post.author?.name || "Anonymous"} + +

+ + {/* Content Section */} +
+ {post.content ? ( +

{post.content}

+ ) : ( +

+ No content available for this post. +

+ )} +
+
+ + {/* Delete Button */} +
+ +
+
+ ); +} diff --git a/examples/prisma-postgres/app/posts/new/page.tsx b/examples/prisma-postgres/app/posts/new/page.tsx new file mode 100644 index 00000000000000..0093ae98ea1abd --- /dev/null +++ b/examples/prisma-postgres/app/posts/new/page.tsx @@ -0,0 +1,98 @@ +export const dynamic = "force-dynamic"; // This disables SSG and ISR + +import Form from "next/form"; +import prisma from "@/lib/prisma"; +import { revalidatePath } from "next/cache"; +import { redirect } from "next/navigation"; + +export default function NewPost() { + async function createPost(formData: FormData) { + "use server"; + + const authorEmail = (formData.get("authorEmail") as string) || undefined; + const title = formData.get("title") as string; + const content = formData.get("content") as string; + + const postData = authorEmail + ? { + title, + content, + author: { + connect: { + email: authorEmail, + }, + }, + } + : { + title, + content, + }; + + await prisma.post.create({ + data: postData, + }); + + revalidatePath("/posts"); + redirect("/posts"); + } + + return ( +
+

Create New Post

+
+
+ + +
+
+ +