first commit
Some checks failed
Test examples / Test Examples (20) (push) Has been cancelled
Test examples / Test Examples (22) (push) Has been cancelled
Lock Threads / action (push) Has been cancelled
Trigger Release / start (push) Has been cancelled
Stale issue handler / stale (push) Has been cancelled
Update Font Data / create-pull-request (push) Has been cancelled
build-and-deploy / deploy-target (push) Has been cancelled
build-and-deploy / build (push) Has been cancelled
build-and-deploy / stable - aarch64-unknown-linux-musl - node@16 (push) Has been cancelled
build-and-deploy / stable - x86_64-unknown-linux-musl - node@16 (push) Has been cancelled
build-and-deploy / stable - aarch64-unknown-linux-gnu - node@16 (push) Has been cancelled
build-and-deploy / stable - x86_64-unknown-linux-gnu - node@16 (push) Has been cancelled
build-and-deploy / stable - aarch64-pc-windows-msvc - node@16 (push) Has been cancelled
build-and-deploy / stable - x86_64-pc-windows-msvc - node@16 (push) Has been cancelled
build-and-deploy / stable - aarch64-apple-darwin - node@16 (push) Has been cancelled
build-and-deploy / stable - x86_64-apple-darwin - node@16 (push) Has been cancelled
build-and-deploy / build-wasm (nodejs) (push) Has been cancelled
build-and-deploy / build-wasm (web) (push) Has been cancelled
build-and-deploy / Deploy preview tarball (push) Has been cancelled
build-and-deploy / Potentially publish release (push) Has been cancelled
build-and-deploy / publish-turbopack-npm-packages (push) Has been cancelled
build-and-deploy / Deploy examples (push) Has been cancelled
build-and-deploy / thank you, build (push) Has been cancelled
build-and-deploy / Upload Turbopack Bytesize metrics to Datadog (push) Has been cancelled
Rspack Next.js development integration tests / Rspack integration tests (push) Has been cancelled
Rspack Next.js production integration tests / Rspack integration tests (push) Has been cancelled
Turbopack Next.js development integration tests / Next.js integration tests (push) Has been cancelled
Turbopack Next.js production integration tests / Next.js integration tests (push) Has been cancelled
Update Rspack test manifest / Update and upload Rspack development test manifest (push) Has been cancelled
Update Rspack test manifest / Update and upload Rspack production test manifest (push) Has been cancelled
Upload bundler test manifests to areweturboyet.com / Upload test results (push) Has been cancelled
Update React / create-pull-request (push) Has been cancelled
test-e2e-project-reset-cron / reset-test-project (push) Has been cancelled
Notify about the top 15 issues/PRs/feature requests (most reacted) in the last 90 days / run (push) Has been cancelled

This commit is contained in:
Arian Tron
2026-03-10 19:37:31 +03:30
commit 61f56f997c
27684 changed files with 2784175 additions and 0 deletions

44
examples/prisma-postgres/.gitignore vendored Normal file
View File

@@ -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

View File

@@ -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
```
<details>
<summary>Expand for <code>yarn</code>, <code>pnpm</code> or <code>bun</code></summary>
```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
```
</details>
Navigate into the created app:
```bash
cd ./my-prisma-postgres-app
```
Install the dependencies if you haven't already:
```bash
# Using npm
npm install
```
<details>
<summary>Expand for <code>yarn</code>, <code>pnpm</code> or <code>bun</code></summary>
```bash
# Using yarn
yarn install
# Using pnpm
pnpm install
# Using bun
bun install
```
</details>
### 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:
<details>
<summary>CLI output</summary>
```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
```
</details>
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
```
<details>
<summary>Expand for <code>yarn</code>, <code>pnpm</code> or <code>bun</code></summary>
```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
```
</details>
### 5. Seed the database
Add initial data to your database:
```bash
# Using npm
npx prisma db seed
```
<details>
<summary>Expand for <code>yarn</code>, <code>pnpm</code> or <code>bun</code></summary>
```bash
# Using yarn
yarn prisma db seed
# Using pnpm
pnpm prisma db seed
# Using bun
bun prisma db seed
```
</details>
### 6. Run the app
Start the development server:
```bash
# Using npm
npm run dev
```
<details>
<summary>Expand for <code>yarn</code>, <code>pnpm</code> or <code>bun</code></summary>
```bash
# Using yarn
yarn dev
# Using pnpm
pnpm run dev
# Using bun
bun run dev
```
</details>
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)

View File

@@ -0,0 +1,32 @@
"use client";
import Link from "next/link";
export default function Header() {
return (
<header className="w-full bg-white shadow-md py-4 px-8">
<nav className="flex justify-between items-center">
<Link
href="/"
className="text-xl font-bold text-gray-800 hover:text-blue-600 transition-colors"
>
Superblog
</Link>
<div className="space-x-4">
<Link href="/posts" className="text-blue-600 hover:underline">
Posts
</Link>
<Link href="/posts/new" className="text-blue-600 hover:underline">
New Post
</Link>
<Link
href="/users/new"
className="bg-blue-500 text-white px-4 py-2 rounded-lg hover:bg-blue-600 transition"
>
New User
</Link>
</div>
</nav>
</header>
);
}

View File

@@ -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 });
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

View File

@@ -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;
}

View File

@@ -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 (
<html lang="en" className="h-full">
<body className="h-full overflow-hidden">
<div className="min-h-screen flex flex-col">
<Header />
<main className="flex-1 overflow-auto">{children}</main>
</div>
</body>
</html>
);
}

View File

@@ -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 (
<div className="min-h-screen bg-gray-50 flex flex-col items-center justify-center -mt-16 p-8">
<h1 className="text-5xl font-extrabold mb-12 text-[#333333]">
Recent Posts
</h1>
<div className="grid gap-8 md:grid-cols-2 lg:grid-cols-3 w-full max-w-6xl">
{posts.map((post) => (
<Link key={post.id} href={`/posts/${post.id}`} className="group">
<div className="border rounded-lg shadow-md bg-white p-6 hover:shadow-lg transition-shadow duration-300">
<h2 className="text-2xl font-semibold text-blue-600 group-hover:underline mb-2">
{post.title}
</h2>
<p className="text-sm text-gray-500">
by {post.author ? post.author.name : "Anonymous"}
</p>
<p className="text-xs text-gray-400 mb-4">
{new Date(post.createdAt).toLocaleDateString("en-US", {
year: "numeric",
month: "long",
day: "numeric",
})}
</p>
<div className="relative">
<p className="text-gray-700 leading-relaxed line-clamp-2">
{post.content || "No content available."}
</p>
<div className="absolute bottom-0 left-0 w-full h-12 bg-gradient-to-t from-gray-50 to-transparent" />
</div>
</div>
</Link>
))}
</div>
</div>
);
}

View File

@@ -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 (
<div className="min-h-screen bg-gray-50 flex flex-col items-center justify-center p-8">
<article className="max-w-3xl w-full bg-white shadow-lg rounded-lg p-8">
{/* Post Title */}
<h1 className="text-5xl font-extrabold text-blue-600 mb-4">
{post.title}
</h1>
{/* Author Information */}
<p className="text-lg text-gray-600 mb-4">
by{" "}
<span className="font-medium text-gray-800">
{post.author?.name || "Anonymous"}
</span>
</p>
{/* Content Section */}
<div className="text-lg text-gray-800 leading-relaxed space-y-6 border-t pt-6">
{post.content ? (
<p>{post.content}</p>
) : (
<p className="italic text-gray-500">
No content available for this post.
</p>
)}
</div>
</article>
{/* Delete Button */}
<form action={deletePost} className="mt-6">
<button
type="submit"
className="px-6 py-3 bg-red-500 text-white font-semibold rounded-lg hover:bg-red-600 transition-colors"
>
Delete Post
</button>
</form>
</div>
);
}

View File

@@ -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 (
<div className="max-w-2xl mx-auto p-4">
<h1 className="text-2xl font-bold mb-6">Create New Post</h1>
<Form action={createPost} className="space-y-6">
<div>
<label
htmlFor="title"
className="flex text-lg font-medium mb-2 items-center"
>
Title
<span className="ml-2 px-2 py-1 text-xs font-semibold text-white bg-gray-500 rounded-lg">
Required
</span>
</label>
<input
type="text"
id="title"
name="title"
required
placeholder="Enter your post title ..."
className="w-full px-4 py-2 border rounded-lg"
/>
</div>
<div>
<label htmlFor="content" className="block text-lg font-medium mb-2">
Content
</label>
<textarea
id="content"
name="content"
placeholder="Write your post content here ..."
rows={6}
className="w-full px-4 py-2 border rounded-lg"
/>
</div>
<div>
<label
htmlFor="authorEmail"
className="block text-lg font-medium mb-2"
>
Author
</label>
<input
type="text"
id="authorEmail"
name="authorEmail"
placeholder="Enter the email of the author here ..."
className="w-full px-4 py-2 border rounded-lg"
/>
</div>
<button
type="submit"
className="w-full bg-blue-500 text-white py-3 rounded-lg hover:bg-blue-600"
>
Create Post
</button>
</Form>
</div>
);
}

View File

@@ -0,0 +1,126 @@
"use client";
import { useSearchParams } from "next/navigation";
import { useEffect, useState, Suspense } from "react";
import Link from "next/link";
interface Post {
id: number;
title: string;
content?: string;
createdAt: string;
author?: {
name: string;
};
}
// Disable static generation
export const dynamic = "force-dynamic";
function PostsList() {
const searchParams = useSearchParams();
const page = parseInt(searchParams.get("page") || "1");
const [posts, setPosts] = useState<Post[]>([]);
const [totalPages, setTotalPages] = useState(1);
const [isLoading, setIsLoading] = useState(true);
useEffect(() => {
async function fetchPosts() {
setIsLoading(true);
try {
const res = await fetch(`/api/posts?page=${page}`);
if (!res.ok) {
throw new Error("Failed to fetch posts");
}
const data = await res.json();
setPosts(data.posts);
setTotalPages(data.totalPages);
} catch (error) {
console.error("Error fetching posts:", error);
} finally {
setIsLoading(false);
}
}
fetchPosts();
}, [page]);
return (
<>
{isLoading ? (
<div className="flex items-center justify-center space-x-2 min-h-[200px]">
<div className="w-6 h-6 border-4 border-blue-500 border-t-transparent rounded-full animate-spin"></div>
<p className="text-gray-600">Loading...</p>
</div>
) : (
<>
{posts.length === 0 ? (
<p className="text-gray-600">No posts available.</p>
) : (
<ul className="space-y-6 w-full max-w-4xl mx-auto">
{posts.map((post) => (
<li
key={post.id}
className="border p-6 rounded-lg shadow-md bg-white"
>
<Link
href={`/posts/${post.id}`}
className="text-2xl font-semibold text-blue-600 hover:underline"
>
{post.title}
</Link>
<p className="text-sm text-gray-500">
by {post.author?.name || "Anonymous"}
</p>
<p className="text-xs text-gray-400">
{new Date(post.createdAt).toLocaleDateString("en-US", {
year: "numeric",
month: "long",
day: "numeric",
})}
</p>
</li>
))}
</ul>
)}
{/* Pagination Controls */}
<div className="flex justify-center space-x-4 mt-8">
{page > 1 && (
<Link href={`/posts?page=${page - 1}`}>
<button className="px-4 py-2 bg-gray-200 rounded hover:bg-gray-300">
Previous
</button>
</Link>
)}
{page < totalPages && (
<Link href={`/posts?page=${page + 1}`}>
<button className="px-4 py-2 bg-gray-200 rounded hover:bg-gray-300">
Next
</button>
</Link>
)}
</div>
</>
)}
</>
);
}
export default function PostsPage() {
return (
<div className="min-h-screen bg-gray-50 flex flex-col items-center justify-start p-8">
<Suspense
fallback={
<div className="flex items-center justify-center min-h-screen">
<div className="w-10 h-10 border-4 border-blue-500 border-t-transparent rounded-full animate-spin"></div>
<p className="ml-3 text-gray-600">Loading page...</p>
</div>
}
>
<PostsList />
</Suspense>
</div>
);
}

View File

@@ -0,0 +1,187 @@
export default function SetupPage() {
return (
<div className="min-h-screen bg-gray-100 flex flex-col items-center py-12 px-6">
<div className="max-w-3xl w-full bg-white p-8 rounded-lg shadow-md border border-gray-300 overflow-y-auto h-[90vh]">
<div className="max-w-3xl bg-white p-8 rounded-lg shadow-md border border-gray-300">
<h1 className="text-3xl font-bold text-gray-900 mb-6">
🛠 Prisma Postgres Setup Guide
</h1>
<p className="text-gray-700">
Congratulations, you just deployed the Next.js + Prisma Postgres
starter template 🎉
</p>
<p className="text-gray-700 mt-2">
To make the app work, you need to connect it to a database.
</p>
{/* Step 1 */}
<div className="mt-6">
<h2 className="text-2xl font-semibold text-gray-900">
1. Create a Prisma Postgres instance
</h2>
<p className="text-gray-700 mt-2">
Create a new Prisma Postgres database instance:
</p>
<ol className="list-decimal pl-6 mt-2 space-y-1 text-gray-700">
<li>
Go to{" "}
<a
href="https://console.prisma.io"
className="text-blue-600 hover:underline"
target="_blank"
rel="noreferrer"
>
Prisma Console
</a>
.
</li>
<li>
Click <strong>New project</strong> to create a new project.
</li>
<li>
Enter a name for your project in the <strong>Name</strong>{" "}
field.
</li>
<li>
Inside the <strong>Prisma Postgres</strong> section, click{" "}
<strong>Get started</strong>.
</li>
<li>
Choose a region close to your location from the{" "}
<strong>Region</strong> dropdown.
</li>
<li>
Click <strong>Create project</strong> to set up your database.
</li>
</ol>
</div>
{/* Step 2 */}
<div className="mt-6">
<h2 className="text-2xl font-semibold text-gray-900">
2. Add the Prisma integration token to Netlify
</h2>
<p className="text-gray-700 mt-2">
When you created the site with this template, you already
installed the{" "}
<a
href="https://app.netlify.com/extensions/prisma-postgres"
className="text-blue-600 hover:underline"
target="_blank"
rel="noreferrer"
>
Prisma Postgres extension
</a>{" "}
in your Netlify account. Next, you need to copy Prisma&apos;s
integration token into the extension in your Neltify Dashboard:
</p>
<ol className="list-decimal pl-6 mt-2 space-y-1 text-gray-700">
<li>
In the{" "}
<a
href="https://console.prisma.io"
className="text-blue-600 hover:underline"
target="_blank"
rel="noreferrer"
>
Prisma Console
</a>
, navigate to the root of your Workspace and click the{" "}
<strong>Integrations</strong> tab in the left-hand sidenav.
</li>
<li>
In the <strong>Configure Team settings</strong> section, click
the <strong>Generate integration token</strong> button.
</li>
<li>
Copy the generated token, you now need to add it to the
team-level extension configuration in your Netlify account as
described in the next step.
</li>
<li>
In your Netlify account, click the <strong>Extensions</strong>{" "}
tab in the left-hand sidenav and navigate to the installed{" "}
<strong>Prisma Postgres</strong> extension. Then find the text
field for the <strong>Integration token</strong>, paste the
token from the previous step into it and click{" "}
<strong>Save</strong>.
</li>
</ol>
</div>
{/* Step 3 */}
<div className="mt-6">
<h2 className="text-2xl font-semibold text-gray-900">
3. Connect Netlify site with Prisma Postgres instance
</h2>
<p className="text-gray-700 mt-2">
In this step, you will connect your Netlify site with the Prisma
Postgres instance you just created:
</p>
<ol className="list-decimal pl-6 mt-2 space-y-1 text-gray-700">
<li>
In your Netlify Dashboard, click on the <strong>Sites</strong>{" "}
item in the left-hand sidenav and click on the site that was
deployed with this template.
</li>
<li>
In the <strong>Extensions</strong> section, click on{" "}
<strong>Prisma Postgres</strong>.
</li>
<li>
Find the <strong>Project</strong> dropdown and select the one
project created in <strong>Step 1</strong> before.
</li>
<li>
In the next two dropdowns for{" "}
<strong>Production environment</strong> and{" "}
<strong>Preview environment</strong> select{" "}
<strong>Development</strong>. Then click <strong>Save</strong>.
(At this stage, you can theoretically connect different database
instances to the different environments in Netlify. For the
purpose of this demo, we are just connecting both environments
to the <strong>Development</strong> database in Prisma Console).
</li>
</ol>
</div>
{/* Step 4 */}
<div className="mt-6">
<h2 className="text-2xl font-semibold text-gray-900">
4. Re-deploy the site in Netlify
</h2>
<p className="text-gray-700 mt-2">
Your site is now fully configured to load data from the Prisma
Postgres database you just created, the last thing you need to do
is re-deploy the site via the Netlify Dashboard.
</p>
<ol className="list-decimal pl-6 mt-2 space-y-1 text-gray-700">
<li>
In your Netlify Dashboard, click on the <strong>Sites</strong>{" "}
item in the left-hand sidenav and click on the site that was
deployed with this template.
</li>
<li>
Find the <strong>Project</strong> dropdown and select the one
project created in <strong>Step 1</strong> before.
</li>
<li>
Click on the <strong>Deploys</strong> tab on the left, click the{" "}
<strong>Trigger deploy</strong> button, and finally click{" "}
<strong>Clear cache and deploy site</strong>.
</li>
</ol>
<p className="text-gray-700 mt-2">
{" "}
Once the deployment is ready, you can click on{" "}
<strong>Open production deploy</strong> and use the app by
creating users and posts via the UI 🎉
</p>
</div>
</div>
</div>
</div>
);
}

View File

@@ -0,0 +1,65 @@
export const dynamic = "force-dynamic"; // This disables SSG and ISR
import prisma from "@/lib/prisma";
import { redirect } from "next/navigation";
import Form from "next/form";
export default function NewUser() {
async function createUser(formData: FormData) {
"use server";
const name = formData.get("name") as string;
const email = formData.get("email") as string;
await prisma.user.create({
data: { name, email },
});
redirect("/");
}
return (
<div className="max-w-2xl mx-auto p-6 bg-white rounded-lg shadow-md mt-12">
<h1 className="text-3xl font-bold mb-6">Create New User</h1>
<Form action={createUser} className="space-y-6">
<div>
<label htmlFor="name" className="block text-lg font-medium mb-2">
Name
</label>
<input
type="text"
id="name"
name="name"
placeholder="Enter user name ..."
className="w-full px-4 py-2 border rounded-lg"
/>
</div>
<div>
<label
htmlFor="email"
className="flex text-lg font-medium mb-2 items-center"
>
Email
<span className="ml-2 px-2 py-1 text-xs font-semibold text-white bg-gray-500 rounded-lg">
Required
</span>
</label>
<input
type="email"
id="email"
name="email"
required
placeholder="Enter user email ..."
className="w-full px-4 py-2 border rounded-lg"
/>
</div>
<button
type="submit"
className="w-full bg-blue-500 text-white py-3 rounded-lg hover:bg-blue-600"
>
Create User
</button>
</Form>
</div>
);
}

View File

@@ -0,0 +1,10 @@
import { PrismaClient } from "./generated/prisma-client";
import { withAccelerate } from "@prisma/extension-accelerate";
const prisma = new PrismaClient().$extends(withAccelerate());
const globalForPrisma = global as unknown as { prisma: typeof prisma };
if (process.env.NODE_ENV !== "production") globalForPrisma.prisma = prisma;
export default prisma;

View File

@@ -0,0 +1,7 @@
import type { NextConfig } from "next";
const nextConfig: NextConfig = {
/* config options here */
};
export default nextConfig;

View File

@@ -0,0 +1,30 @@
{
"private": true,
"scripts": {
"dev": "next dev --turbopack",
"build": "next build",
"start": "next start",
"lint": "eslint .",
"postinstall": "prisma generate --no-engine"
},
"prisma": {
"seed": "tsx prisma/seed.ts"
},
"dependencies": {
"@prisma/client": "latest",
"@prisma/extension-accelerate": "latest",
"next": "latest",
"react": "^19.0.0",
"react-dom": "^19.0.0"
},
"devDependencies": {
"@types/node": "^20",
"@types/react": "^19",
"@types/react-dom": "^19",
"postcss": "^8",
"prisma": "latest",
"tailwindcss": "^3.4.1",
"tsx": "^4.19.2",
"typescript": "^5"
}
}

View File

@@ -0,0 +1,8 @@
/** @type {import('postcss-load-config').Config} */
const config = {
plugins: {
tailwindcss: {},
},
};
export default config;

View File

@@ -0,0 +1,27 @@
generator client {
provider = "prisma-client-js"
output = "../lib/generated/prisma-client"
}
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())
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
title String
content String?
published Boolean @default(false)
authorId Int
author User @relation(fields: [authorId], references: [id])
}

View File

@@ -0,0 +1,153 @@
import { PrismaClient } from "../lib/generated/prisma-client";
const prisma = new PrismaClient();
async function main() {
// Create 5 users
await prisma.user.createMany({
data: [
{ email: "alice@example.com", name: "Alice" },
{ email: "bob@example.com", name: "Bob" },
{ email: "charlie@example.com", name: "Charlie" },
{ email: "diana@example.com", name: "Diana" },
{ email: "edward@example.com", name: "Edward" },
],
});
// Find all users to get their IDs
const userRecords = await prisma.user.findMany();
const userIdMapping = {
alice: userRecords.find((user) => user.email === "alice@example.com")?.id,
bob: userRecords.find((user) => user.email === "bob@example.com")?.id,
charlie: userRecords.find((user) => user.email === "charlie@example.com")
?.id,
diana: userRecords.find((user) => user.email === "diana@example.com")?.id,
edward: userRecords.find((user) => user.email === "edward@example.com")?.id,
};
// Create 15 posts distributed among users
await prisma.post.createMany({
data: [
// Alice's posts
{
title: "Getting Started with TypeScript and Prisma",
content:
"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Fusce id erat a lorem tincidunt ultricies. Vivamus porta bibendum nulla vel accumsan.",
published: true,
authorId: userIdMapping.alice!,
},
{
title: "How ORMs Simplify Complex Queries",
content:
"Duis sagittis urna ut sapien tristique convallis. Aenean vel ligula felis. Phasellus bibendum sem at elit dictum volutpat.",
published: false,
authorId: userIdMapping.alice!,
},
// Bob's posts
{
title: "Mastering Prisma: Efficient Database Migrations",
content:
"Ut ullamcorper nec erat id auctor. Nullam nec ligula in ex feugiat tincidunt. Cras accumsan vehicula tortor ut eleifend.",
published: true,
authorId: userIdMapping.bob!,
},
{
title: "Best Practices for Type Safety in ORMs",
content:
"Aliquam erat volutpat. Suspendisse potenti. Maecenas fringilla elit vel eros laoreet, et tempor sapien vulputate.",
published: true,
authorId: userIdMapping.bob!,
},
{
title: "TypeScript Utility Types for Database Models",
content:
"Donec ac magna facilisis, vestibulum ligula at, elementum nisl. Morbi volutpat eget velit eu egestas.",
published: false,
authorId: userIdMapping.bob!,
},
// Charlie's posts (no posts for Charlie)
// Diana's posts
{
title: "Exploring Database Indexes and Their Performance Impact",
content:
"Vivamus ac velit tincidunt, sollicitudin erat quis, fringilla enim. Aenean posuere est a risus placerat suscipit.",
published: true,
authorId: userIdMapping.diana!,
},
{
title: "Choosing the Right Database for Your TypeScript Project",
content:
"Sed vel suscipit lorem. Duis et arcu consequat, sagittis justo quis, pellentesque risus. Curabitur sed consequat est.",
published: false,
authorId: userIdMapping.diana!,
},
{
title: "Designing Scalable Schemas with Prisma",
content:
"Phasellus ut erat nec elit ultricies egestas. Vestibulum rhoncus urna eget magna varius pharetra.",
published: true,
authorId: userIdMapping.diana!,
},
{
title: "Handling Relations Between Models in ORMs",
content:
"Integer luctus ac augue at tristique. Curabitur varius nisl vitae mi fringilla, vel tincidunt nunc dictum.",
published: false,
authorId: userIdMapping.diana!,
},
// Edward's posts
{
title: "Why TypeORM Still Has Its Place in 2025",
content:
"Morbi non arcu nec velit cursus feugiat sit amet sit amet mi. Etiam porttitor ligula id sem molestie, in tempor arcu bibendum.",
published: true,
authorId: userIdMapping.edward!,
},
{
title: "NoSQL vs SQL: The Definitive Guide for Developers",
content:
"Suspendisse a ligula sit amet risus ullamcorper tincidunt. Curabitur tincidunt, sapien id fringilla auctor, risus libero gravida odio, nec volutpat libero orci nec lorem.",
published: true,
authorId: userIdMapping.edward!,
},
{
title: "Optimizing Queries with Prismas Select and Include",
content:
"Proin vel diam vel nisi facilisis malesuada. Sed vitae diam nec magna mollis commodo a vitae nunc.",
published: false,
authorId: userIdMapping.edward!,
},
{
title: "PostgreSQL Optimizations Every Developer Should Know",
content:
"Nullam mollis quam sit amet lacus interdum, at suscipit libero pellentesque. Suspendisse in mi vitae magna finibus pretium.",
published: true,
authorId: userIdMapping.edward!,
},
{
title: "Scaling Applications with Partitioned Tables in PostgreSQL",
content:
"Cras vitae tortor in mauris tristique elementum non id ipsum. Nunc vitae pulvinar purus.",
published: true,
authorId: userIdMapping.edward!,
},
],
});
console.log("Seeding completed.");
}
main()
.then(async () => {
await prisma.$disconnect();
})
.catch(async (e) => {
console.error(e);
await prisma.$disconnect();
process.exit(1);
});

View File

@@ -0,0 +1,18 @@
import type { Config } from "tailwindcss";
export default {
content: [
"./pages/**/*.{js,ts,jsx,tsx,mdx}",
"./components/**/*.{js,ts,jsx,tsx,mdx}",
"./app/**/*.{js,ts,jsx,tsx,mdx}",
],
theme: {
extend: {
colors: {
background: "var(--background)",
foreground: "var(--foreground)",
},
},
},
plugins: [],
} satisfies Config;

View File

@@ -0,0 +1,27 @@
{
"compilerOptions": {
"target": "ES2017",
"lib": ["dom", "dom.iterable", "esnext"],
"allowJs": true,
"skipLibCheck": true,
"strict": true,
"noEmit": true,
"esModuleInterop": true,
"module": "esnext",
"moduleResolution": "bundler",
"resolveJsonModule": true,
"isolatedModules": true,
"jsx": "react-jsx",
"incremental": true,
"plugins": [
{
"name": "next"
}
],
"paths": {
"@/*": ["./*"]
}
},
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
"exclude": ["node_modules"]
}