{post.title}
{post.content}
Welcome to the comprehensive Next.js documentation. Next.js is a React framework that enables functionality such as server-side rendering and generating static websites for React based web applications.
Next.js is a full-stack React framework that provides everything you need to build production-ready web applications. It features automatic optimization, TypeScript support, and a powerful routing system.
Key features of Next.js:
Make sure you have Node.js (version 18.17 or later) and npm installed:
# Check Node.js version
node --version
# Check npm version
npm --version
# Create a new Next.js app
npx create-next-app@latest my-next-app
# Or with TypeScript
npx create-next-app@latest my-next-app --typescript
# Navigate to the project
cd my-next-app
# Start the development server
npm run dev
# Initialize npm project
npm init -y
# Install Next.js
npm install next react react-dom
# Install additional packages
npm install @types/node @types/react @types/react-dom typescript eslint eslint-config-next
# Create necessary directories
mkdir pages components styles public
{
"scripts": {
"dev": "next dev",
"build": "next build",
"start": "next start",
"lint": "next lint",
"export": "next export"
}
}
my-next-app/
├── app/
│ ├── layout.tsx
│ ├── page.tsx
│ ├── globals.css
│ ├── about/
│ │ └── page.tsx
│ └── api/
│ └── users/
│ └── route.ts
├── components/
│ ├── Header.tsx
│ ├── Footer.tsx
│ └── Button.tsx
├── lib/
│ └── utils.ts
├── public/
│ ├── favicon.ico
│ └── images/
├── styles/
│ └── Home.module.css
├── next.config.js
├── package.json
├── tsconfig.json
└── tailwind.config.js
my-next-app/
├── pages/
│ ├── _app.tsx
│ ├── _document.tsx
│ ├── index.tsx
│ ├── about.tsx
│ └── api/
│ └── users.ts
├── components/
├── public/
├── styles/
├── next.config.js
└── package.json
// app/layout.tsx
import type { Metadata } from 'next'
import { Inter } from 'next/font/google'
import './globals.css'
const inter = Inter({ subsets: ['latin'] })
export const metadata: Metadata = {
title: 'My Next.js App',
description: 'Generated by create next app',
}
export default function RootLayout({
children,
}: {
children: React.ReactNode
}) {
return (
{children}
)
}
// app/page.tsx
import Link from 'next/link'
export default function Home() {
return (
Welcome to Next.js
This is the home page.
Go to About
)
}
// app/blog/[slug]/page.tsx
import { notFound } from 'next/navigation'
interface PageProps {
params: {
slug: string
}
}
export default function BlogPost({ params }: PageProps) {
const { slug } = params
// Fetch blog post data
const post = getBlogPost(slug)
if (!post) {
notFound()
}
return (
{post.title}
{post.content}
)
}
export async function generateStaticParams() {
const posts = await getAllBlogPosts()
return posts.map((post) => ({
slug: post.slug,
}))
}
// pages/_app.tsx
import type { AppProps } from 'next/app'
import '../styles/globals.css'
export default function App({ Component, pageProps }: AppProps) {
return
}
// pages/_document.tsx import { Html, Head, Main, NextScript } from 'next/document' export default function Document() { return () }
// pages/index.tsx
import type { NextPage } from 'next'
import Link from 'next/link'
const Home: NextPage = () => {
return (
Welcome to Next.js
About
)
}
export default Home
// app/users/page.tsx
async function getUsers() {
const res = await fetch('https://api.example.com/users')
if (!res.ok) {
throw new Error('Failed to fetch users')
}
return res.json()
}
export default async function UsersPage() {
const users = await getUsers()
return (
Users
{users.map((user) => (
- {user.name}
))}
)
}
// components/UserList.tsx
'use client'
import { useState, useEffect } from 'react'
export default function UserList() {
const [users, setUsers] = useState([])
const [loading, setLoading] = useState(true)
useEffect(() => {
async function fetchUsers() {
try {
const res = await fetch('/api/users')
const data = await res.json()
setUsers(data)
} catch (error) {
console.error('Failed to fetch users:', error)
} finally {
setLoading(false)
}
}
fetchUsers()
}, [])
if (loading) return Loading...
return (
{users.map((user) => (
- {user.name}
))}
)
}
# Install SWR
npm install swr
// components/UserList.tsx
'use client'
import useSWR from 'swr'
const fetcher = (url: string) => fetch(url).then((res) => res.json())
export default function UserList() {
const { data, error, isLoading } = useSWR('/api/users', fetcher)
if (error) return Failed to load
if (isLoading) return Loading...
return (
{data.map((user) => (
- {user.name}
))}
)
}
// pages/users.tsx
import type { GetServerSideProps, InferGetServerSidePropsType } from 'next'
type User = {
id: number
name: string
}
type UsersPageProps = {
users: User[]
}
export default function UsersPage({ users }: UsersPageProps) {
return (
Users
{users.map((user) => (
- {user.name}
))}
)
}
export const getServerSideProps: GetServerSideProps = async () => {
const res = await fetch('https://api.example.com/users')
const users: User[] = await res.json()
return {
props: {
users,
},
}
}
// pages/posts/[id].tsx
import type { GetStaticProps, GetStaticPaths, InferGetStaticPropsType } from 'next'
type Post = {
id: number
title: string
content: string
}
type PostPageProps = {
post: Post
}
export default function PostPage({ post }: PostPageProps) {
return (
{post.title}
{post.content}
)
}
export const getStaticPaths: GetStaticPaths = async () => {
const res = await fetch('https://api.example.com/posts')
const posts: Post[] = await res.json()
const paths = posts.map((post) => ({
params: { id: post.id.toString() },
}))
return {
paths,
fallback: 'blocking',
}
}
export const getStaticProps: GetStaticProps = async ({ params }) => {
const res = await fetch(`https://api.example.com/posts/${params.id}`)
const post: Post = await res.json()
return {
props: {
post,
},
revalidate: 60, // Re-generate the page every 60 seconds
}
}
// app/api/users/route.ts
import { NextRequest, NextResponse } from 'next/server'
export async function GET(request: NextRequest) {
const users = [
{ id: 1, name: 'John Doe' },
{ id: 2, name: 'Jane Doe' },
]
return NextResponse.json(users)
}
export async function POST(request: NextRequest) {
const body = await request.json()
const newUser = { id: Date.now(), ...body }
// Save to database
// await saveUser(newUser)
return NextResponse.json(newUser, { status: 201 })
}
// pages/api/users.ts
import type { NextApiRequest, NextApiResponse } from 'next'
type User = {
id: number
name: string
}
export default function handler(
req: NextApiRequest,
res: NextApiResponse
) {
if (req.method === 'GET') {
const users: User[] = [
{ id: 1, name: 'John Doe' },
{ id: 2, name: 'Jane Doe' },
]
res.status(200).json(users)
} else if (req.method === 'POST') {
const newUser: User = { id: Date.now(), name: req.body.name }
res.status(201).json(newUser)
} else {
res.setHeader('Allow', ['GET', 'POST'])
res.status(405).end(`Method ${req.method} Not Allowed`)
}
}
// app/api/users/[id]/route.ts
import { NextRequest, NextResponse } from 'next/server'
export async function GET(
request: NextRequest,
{ params }: { params: { id: string } }
) {
const id = params.id
// Fetch user from database
const user = await getUserById(id)
if (!user) {
return NextResponse.json({ error: 'User not found' }, { status: 404 })
}
return NextResponse.json(user)
}
export async function PUT(
request: NextRequest,
{ params }: { params: { id: string } }
) {
const id = params.id
const body = await request.json()
// Update user in database
const updatedUser = await updateUser(id, body)
if (!updatedUser) {
return NextResponse.json({ error: 'User not found' }, { status: 404 })
}
return NextResponse.json(updatedUser)
}
export async function DELETE(
request: NextRequest,
{ params }: { params: { id: string } }
) {
const id = params.id
// Delete user from database
const success = await deleteUser(id)
if (!success) {
return NextResponse.json({ error: 'User not found' }, { status: 404 })
}
return NextResponse.json({ message: 'User deleted successfully' })
}
// app/api/protected/route.ts
import { NextRequest, NextResponse } from 'next/server'
import { getToken } from 'next-auth/jwt'
export async function GET(request: NextRequest) {
// Get the token from the request
const token = await getToken({ req: request })
// If no token, return unauthorized
if (!token) {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
}
// If token exists, return protected data
return NextResponse.json({
message: 'This is protected data',
user: token
})
}
// app/api/users/route.ts
import { NextRequest, NextResponse } from 'next/server'
export async function POST(request: NextRequest) {
try {
const body = await request.json()
// Validate input
if (!body.name || body.name.trim() === '') {
return NextResponse.json(
{ error: 'Name is required' },
{ status: 400 }
)
}
// Create new user
const newUser = { id: Date.now(), name: body.name }
// Save to database
// await saveUser(newUser)
return NextResponse.json(newUser, { status: 201 })
} catch (error) {
console.error('Error creating user:', error)
return NextResponse.json(
{ error: 'Internal server error' },
{ status: 500 }
)
}
}
// app/blog/[slug]/page.tsx
import { notFound } from 'next/navigation'
interface PageProps {
params: {
slug: string
}
}
// This function runs at build time
export async function generateStaticParams() {
const posts = await getAllBlogPosts()
return posts.map((post) => ({
slug: post.slug,
}))
}
// This function runs at build time for each params
async function getBlogPost(slug: string) {
// Fetch blog post data
const res = await fetch(`https://api.example.com/posts/${slug}`)
if (!res.ok) {
return null
}
return res.json()
}
export default async function BlogPost({ params }: PageProps) {
const { slug } = params
const post = await getBlogPost(slug)
if (!post) {
notFound()
}
return (
{post.title}
{post.content}
)
}
// pages/blog/[slug].tsx
import type { GetStaticProps, GetStaticPaths, InferGetStaticPropsType } from 'next'
type Post = {
id: number
title: string
content: string
slug: string
}
type BlogPostProps = {
post: Post
}
export default function BlogPost({ post }: BlogPostProps) {
return (
{post.title}
{post.content}
)
}
// This function tells Next.js which pages to build at build time
export const getStaticPaths: GetStaticPaths = async () => {
const res = await fetch('https://api.example.com/posts')
const posts: Post[] = await res.json()
const paths = posts.map((post) => ({
params: { slug: post.slug },
}))
return {
paths,
fallback: 'blocking', // or true or false
}
}
// This function fetches data for a specific post at build time
export const getStaticProps: GetStaticProps = async ({ params }) => {
const res = await fetch(`https://api.example.com/posts/${params.slug}`)
const post: Post = await res.json()
return {
props: {
post,
},
revalidate: 60, // Re-generate the page every 60 seconds (ISR)
}
}
// pages/blog/[slug].tsx
import type { GetStaticProps, GetStaticPaths } from 'next'
// ... component code ...
export const getStaticProps: GetStaticProps = async ({ params }) => {
const res = await fetch(`https://api.example.com/posts/${params.slug}`)
const post = await res.json()
return {
props: {
post,
},
revalidate: 60, // Re-generate the page every 60 seconds
}
}
// ... getStaticPaths ...
// app/posts/page.tsx
async function getPosts() {
const res = await fetch('https://api.example.com/posts', {
cache: 'force-cache', // Cache the response
})
if (!res.ok) {
throw new Error('Failed to fetch posts')
}
return res.json()
}
export default async function PostsPage() {
const posts = await getPosts()
return (
Posts
{posts.map((post) => (
-
{post.title}
))}
)
}
// next.config.js
/** @type {import('next').NextConfig} */
const nextConfig = {
output: 'export',
trailingSlash: true,
images: {
unoptimized: true,
},
}
module.exports = nextConfig
// package.json
{
"scripts": {
"build": "next build",
"export": "next export"
}
}
// app/posts/[slug]/page.tsx
import { notFound } from 'next/navigation'
interface PageProps {
params: {
slug: string
}
searchParams: {
[key: string]: string | string[] | undefined
}
}
// This function runs on the server for each request
export default async function BlogPost({ params, searchParams }: PageProps) {
const { slug } = params
// Fetch data on the server for each request
const res = await fetch(`https://api.example.com/posts/${slug}`, {
cache: 'no-store', // Disable caching
})
if (!res.ok) {
notFound()
}
const post = await res.json()
return (
{post.title}
{post.content}
View count: {post.views + 1}
)
}
// pages/posts/[slug].tsx
import type { GetServerSideProps, InferGetServerSidePropsType } from 'next'
type Post = {
id: number
title: string
content: string
views: number
}
type BlogPostProps = {
post: Post
}
export default function BlogPost({ post }: BlogPostProps) {
return (
{post.title}
{post.content}
View count: {post.views}
)
}
// This function runs on the server for each request
export const getServerSideProps: GetServerSideProps = async ({ params, req, res }) => {
const { slug } = params
// Fetch data on the server for each request
const res = await fetch(`https://api.example.com/posts/${slug}`)
if (!res.ok) {
return {
notFound: true,
}
}
const post = await res.json()
// Increment view count
await fetch(`https://api.example.com/posts/${slug}/increment-views`, {
method: 'POST',
})
// Set cache control headers
res.setHeader(
'Cache-Control',
'public, s-maxage=10, stale-while-revalidate=59'
)
return {
props: {
post,
},
}
}
// pages/dashboard.tsx
import type { GetServerSideProps, InferGetServerSidePropsType } from 'next'
import { getSession } from 'next-auth/react'
type DashboardProps = {
user: {
id: string
name: string
email: string
}
}
export default function Dashboard({ user }: DashboardProps) {
return (
Dashboard
Welcome, {user.name}!
)
}
export const getServerSideProps: GetServerSideProps = async (context) => {
const session = await getSession(context)
if (!session) {
return {
redirect: {
destination: '/login',
permanent: false,
},
}
}
// Fetch user data
const res = await fetch(`https://api.example.com/users/${session.user.id}`)
const user = await res.json()
return {
props: {
user,
},
}
}
// pages/search.tsx
import type { GetServerSideProps, InferGetServerSidePropsType } from 'next'
import { useState } from 'react'
type SearchResult = {
id: number
title: string
description: string
}
type SearchPageProps = {
initialResults: SearchResult[]
query: string
}
export default function SearchPage({ initialResults, query }: SearchPageProps) {
const [results, setResults] = useState(initialResults)
const [loading, setLoading] = useState(false)
const handleSearch = async (newQuery: string) => {
setLoading(true)
const res = await fetch(`/api/search?q=${newQuery}`)
const data = await res.json()
setResults(data.results)
setLoading(false)
}
return (
Search Results for "{query}"
{loading ? (
Loading...
) : (
{results.map((result) => (
-
{result.title}
{result.description}
))}
)}
)
}
export const getServerSideProps: GetServerSideProps = async (context) => {
const query = context.query.q as string || ''
if (!query) {
return {
props: {
initialResults: [],
query,
},
}
}
// Fetch initial search results
const res = await fetch(`https://api.example.com/search?q=${query}`)
const data = await res.json()
return {
props: {
initialResults: data.results,
query,
},
}
}
/* styles/Home.module.css */
.container {
padding: 0 2rem;
}
.main {
min-height: 100vh;
padding: 4rem 0;
flex: 1;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
.title {
margin: 0;
line-height: 1.15;
font-size: 4rem;
text-align: center;
}
.description {
margin: 4rem 0;
line-height: 1.5;
font-size: 1.5rem;
text-align: center;
}
.grid {
display: flex;
align-items: center;
justify-content: center;
flex-wrap: wrap;
max-width: 800px;
}
.card {
margin: 1rem;
padding: 1.5rem;
text-align: left;
color: inherit;
text-decoration: none;
border: 1px solid #eaeaea;
border-radius: 10px;
transition: color 0.15s ease, border-color 0.15s ease;
}
.card:hover,
.card:focus,
.card:active {
border-color: #0070f3;
}
.card h2 {
margin: 0 0 1rem 0;
font-size: 1.5rem;
}
.card p {
margin: 0;
font-size: 1.25rem;
line-height: 1.5;
}
// pages/index.tsx
import type { NextPage } from 'next'
import Head from 'next/head'
import styles from '../styles/Home.module.css'
const Home: NextPage = () => {
return (
Create Next App
Welcome to Next.js!
Get started by editing{' '}
pages/index.tsx
)
}
export default Home
# Install Tailwind CSS
npm install -D tailwindcss postcss autoprefixer
npx tailwindcss init -p
// tailwind.config.js
/** @type {import('tailwindcss').Config} */
module.exports = {
content: [
'./pages/**/*.{js,ts,jsx,tsx}',
'./components/**/*.{js,ts,jsx,tsx}',
'./app/**/*.{js,ts,jsx,tsx}',
],
theme: {
extend: {},
},
plugins: [],
}
/* styles/globals.css */
@tailwind base;
@tailwind components;
@tailwind utilities;
// components/Button.tsx
import { ButtonHTMLAttributes } from 'react'
interface ButtonProps extends ButtonHTMLAttributes {
variant?: 'primary' | 'secondary'
size?: 'sm' | 'md' | 'lg'
}
export default function Button({
variant = 'primary',
size = 'md',
children,
className = '',
...props
}: ButtonProps) {
const baseClasses = 'rounded font-medium transition-colors'
const variantClasses = {
primary: 'bg-blue-500 text-white hover:bg-blue-600',
secondary: 'bg-gray-200 text-gray-800 hover:bg-gray-300',
}
const sizeClasses = {
sm: 'px-3 py-1 text-sm',
md: 'px-4 py-2',
lg: 'px-6 py-3 text-lg',
}
return (
)
}
# Install styled-components
npm install styled-components
npm install -D @types/styled-components
// components/StyledButton.tsx
import styled from 'styled-components'
const StyledButton = styled.button<{ variant?: 'primary' | 'secondary' }>`
padding: 0.5rem 1rem;
border-radius: 0.25rem;
font-weight: 500;
transition: background-color 0.2s;
${({ variant = 'primary' }) => {
if (variant === 'primary') {
return `
background-color: #3b82f6;
color: white;
&:hover {
background-color: #2563eb;
}
`
} else {
return `
background-color: #e5e7eb;
color: #1f2937;
&:hover {
background-color: #d1d5db;
}
`
}
}}
`
export default StyledButton
// pages/_app.tsx
import type { AppProps } from 'next/app'
import { createGlobalStyle } from 'styled-components'
const GlobalStyle = createGlobalStyle`
body {
margin: 0;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
`
export default function App({ Component, pageProps }: AppProps) {
return (
<>
>
)
}
# Install Emotion
npm install @emotion/react @emotion/styled
npm install -D @emotion/babel-plugin
// .babelrc
{
"presets": ["next/babel"],
"plugins": ["@emotion/babel-plugin"]
}
// components/EmotionButton.tsx
import styled from '@emotion/styled'
const EmotionButton = styled.button<{ variant?: 'primary' | 'secondary' }>`
padding: 0.5rem 1rem;
border-radius: 0.25rem;
font-weight: 500;
transition: background-color 0.2s;
${({ variant = 'primary' }) => {
if (variant === 'primary') {
return `
background-color: #3b82f6;
color: white;
&:hover {
background-color: #2563eb;
}
`
} else {
return `
background-color: #e5e7eb;
color: #1f2937;
&:hover {
background-color: #d1d5db;
}
`
}
}}
`
export default EmotionButton
# Install NextAuth.js
npm install next-auth
// pages/api/auth/[...nextauth].ts
import NextAuth from 'next-auth'
import GoogleProvider from 'next-auth/providers/google'
import CredentialsProvider from 'next-auth/providers/credentials'
export default NextAuth({
providers: [
GoogleProvider({
clientId: process.env.GOOGLE_CLIENT_ID!,
clientSecret: process.env.GOOGLE_CLIENT_SECRET!,
}),
CredentialsProvider({
name: 'credentials',
credentials: {
email: { label: 'Email', type: 'email' },
password: { label: 'Password', type: 'password' },
},
async authorize(credentials) {
// Add your own logic here to validate credentials
// This is just a placeholder
if (credentials?.email === 'user@example.com' && credentials?.password === 'password') {
return {
id: '1',
name: 'User',
email: 'user@example.com',
}
}
return null
},
}),
],
pages: {
signIn: '/auth/signin',
signUp: '/auth/signup',
},
callbacks: {
async jwt({ token, user }) {
if (user) {
token.id = user.id
}
return token
},
async session({ session, token }) {
if (token) {
session.user.id = token.id as string
}
return session
},
},
session: {
strategy: 'jwt',
},
})
// pages/_app.tsx
import type { AppProps } from 'next/app'
import { SessionProvider } from 'next-auth/react'
import '../styles/globals.css'
export default function App({ Component, pageProps: { session: any } }: AppProps) {
return (
)
}
// pages/auth/signin.tsx
import { useState } from 'react'
import { signIn, getSession } from 'next-auth/react'
import { GetServerSideProps } from 'next'
import { useRouter } from 'next/router'
import Link from 'next/link'
export default function SignIn() {
const [email, setEmail] = useState('')
const [password, setPassword] = useState('')
const [error, setError] = useState('')
const router = useRouter()
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault()
setError('')
const result = await signIn('credentials', {
redirect: false,
email,
password,
})
if (result?.error) {
setError('Invalid email or password')
} else {
router.push('/dashboard')
}
}
return (
Sign In
{error && {error}
}
Or sign in with:
Don't have an account? Sign up
)
}
export const getServerSideProps: GetServerSideProps = async (context) => {
const session = await getSession(context)
if (session) {
return {
redirect: {
destination: '/dashboard',
permanent: false,
},
}
}
return {
props: {},
}
}
// pages/dashboard.tsx
import { useSession } from 'next-auth/react'
import { GetServerSideProps } from 'next'
import { getSession } from 'next-auth/react'
export default function Dashboard() {
const { data: session, status } = useSession({
required: true,
onUnauthenticated() {
// Redirect to sign in page if not authenticated
window.location.href = '/auth/signin'
},
})
if (status === 'loading') {
return Loading...
}
return (
Dashboard
Welcome, {session?.user?.name}!
)
}
export const getServerSideProps: GetServerSideProps = async (context) => {
const session = await getSession(context)
if (!session) {
return {
redirect: {
destination: '/auth/signin',
permanent: false,
},
}
}
return {
props: {},
}
}
// hooks/useAuth.ts
import { useSession } from 'next-auth/react'
import { useRouter } from 'next/router'
export function useAuth(redirectTo?: string) {
const { data: session, status } = useSession()
const router = useRouter()
const isAuthenticated = status === 'authenticated'
const isLoading = status === 'loading'
if (isLoading) {
return { isAuthenticated: false, isLoading: true, session: null }
}
if (!isAuthenticated && redirectTo) {
router.push(redirectTo)
}
return { isAuthenticated, isLoading: false, session }
}
// pages/profile.tsx
import { useAuth } from '../hooks/useAuth'
export default function Profile() {
const { isAuthenticated, isLoading, session } = useAuth('/auth/signin')
if (isLoading) {
return Loading...
}
if (!isAuthenticated) {
return null // Will redirect to sign in
}
return (
Profile
Name: {session?.user?.name}
Email: {session?.user?.email}
)
}
// components/LogoutButton.tsx
import { signOut, useSession } from 'next-auth/react'
export default function LogoutButton() {
const { data: session } = useSession()
const handleLogout = async () => {
await signOut({ redirect: false })
window.location.href = '/'
}
if (!session) {
return null
}
return (
)
}
# Install Prisma
npm install prisma --save-dev
npm install @prisma/client
# Initialize Prisma
npx prisma init
// prisma/schema.prisma
generator client {
provider = "prisma-client-js"
}
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}
model User {
id String @id @default(cuid())
email String @unique
name String?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
posts Post[]
}
model Post {
id String @id @default(cuid())
title String
content String?
published Boolean @default(false)
author User @relation(fields: [authorId], references: [id])
authorId String
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}
// lib/prisma.ts
import { PrismaClient } from '@prisma/client'
const globalForPrisma = globalThis as unknown as {
prisma: PrismaClient | undefined
}
export const prisma = globalForPrisma.prisma ?? new PrismaClient()
if (process.env.NODE_ENV !== 'production') globalForPrisma.prisma = prisma
// app/api/posts/route.ts
import { NextRequest, NextResponse } from 'next/server'
import { prisma } from '@/lib/prisma'
export async function GET(request: NextRequest) {
try {
const posts = await prisma.post.findMany({
include: {
author: {
select: {
id: true,
name: true,
email: true,
},
},
},
orderBy: {
createdAt: 'desc',
},
})
return NextResponse.json(posts)
} catch (error) {
return NextResponse.json(
{ error: 'Failed to fetch posts' },
{ status: 500 }
)
}
}
export async function POST(request: NextRequest) {
try {
const body = await request.json()
const { title, content, authorId } = body
if (!title || !authorId) {
return NextResponse.json(
{ error: 'Title and authorId are required' },
{ status: 400 }
)
}
const post = await prisma.post.create({
data: {
title,
content,
authorId,
},
include: {
author: {
select: {
id: true,
name: true,
email: true,
},
},
},
})
return NextResponse.json(post, { status: 201 })
} catch (error) {
return NextResponse.json(
{ error: 'Failed to create post' },
{ status: 500 }
)
}
}
// app/api/posts/[id]/route.ts
import { NextRequest, NextResponse } from 'next/server'
import { prisma } from '@/lib/prisma'
export async function GET(
request: NextRequest,
{ params }: { params: { id: string } }
) {
try {
const post = await prisma.post.findUnique({
where: { id: params.id },
include: {
author: {
select: {
id: true,
name: true,
email: true,
},
},
},
})
if (!post) {
return NextResponse.json(
{ error: 'Post not found' },
{ status: 404 }
)
}
return NextResponse.json(post)
} catch (error) {
return NextResponse.json(
{ error: 'Failed to fetch post' },
{ status: 500 }
)
}
}
export async function PUT(
request: NextRequest,
{ params }: { params: { id: string } }
) {
try {
const body = await request.json()
const { title, content, published } = body
const post = await prisma.post.update({
where: { id: params.id },
data: {
title,
content,
published,
},
include: {
author: {
select: {
id: true,
name: true,
email: true,
},
},
},
})
return NextResponse.json(post)
} catch (error) {
return NextResponse.json(
{ error: 'Failed to update post' },
{ status: 500 }
)
}
}
export async function DELETE(
request: NextRequest,
{ params }: { params: { id: string } }
) {
try {
await prisma.post.delete({
where: { id: params.id },
})
return NextResponse.json({ message: 'Post deleted successfully' })
} catch (error) {
return NextResponse.json(
{ error: 'Failed to delete post' },
{ status: 500 }
)
}
}
# Install Mongoose
npm install mongoose
// lib/mongodb.ts
import mongoose from 'mongoose'
const MONGODB_URI = process.env.MONGODB_URI!
if (!MONGODB_URI) {
throw new Error('Please define the MONGODB_URI environment variable')
}
let cached = global.mongoose
if (!cached) {
cached = global.mongoose = { conn: null, promise: null }
}
export async function connectToDatabase() {
if (cached.conn) {
return cached.conn
}
if (!cached.promise) {
const opts = {
bufferCommands: false,
}
cached.promise = mongoose.connect(MONGODB_URI, opts).then((mongoose) => {
return mongoose
})
}
try {
cached.conn = await cached.promise
} catch (e) {
cached.promise = null
throw e
}
return cached.conn
}
// models/Post.ts
import mongoose, { Schema, Document, Model } from 'mongoose'
export interface IPost extends Document {
title: string
content: string
published: boolean
author: string
createdAt: Date
updatedAt: Date
}
const PostSchema: Schema = new Schema({
title: {
type: String,
required: true,
},
content: {
type: String,
default: '',
},
published: {
type: Boolean,
default: false,
},
author: {
type: String,
required: true,
},
}, {
timestamps: true,
})
const Post: Model = mongoose.models.Post || mongoose.model('Post', PostSchema)
export default Post
// pages/api/posts.ts
import type { NextApiRequest, NextApiResponse } from 'next'
import connectToDatabase from '@/lib/mongodb'
import Post, { IPost } from '@/models/Post'
export default async function handler(
req: NextApiRequest,
res: NextApiResponse
) {
await connectToDatabase()
if (req.method === 'GET') {
try {
const posts = await Post.find({}).sort({ createdAt: -1 })
res.status(200).json(posts)
} catch (error) {
res.status(500).json({ error: 'Failed to fetch posts' })
}
} else if (req.method === 'POST') {
try {
const post = await Post.create(req.body)
res.status(201).json(post)
} catch (error) {
res.status(500).json({ error: 'Failed to create post' })
}
} else {
res.setHeader('Allow', ['GET', 'POST'])
res.status(405).end(`Method ${req.method} Not Allowed`)
}
}
// lib/db.ts
import { Pool } from 'pg'
const pool = new Pool({
connectionString: process.env.DATABASE_URL,
max: 20, // Maximum number of connections in the pool
idleTimeoutMillis: 30000, // How long a connection can be idle before being closed
connectionTimeoutMillis: 2000, // How long to wait when connecting a new client
})
export default {
query: (text: string, params?: any[]) => pool.query(text, params),
end: () => pool.end(),
}
// pages/api/users.ts
import type { NextApiRequest, NextApiResponse } from 'next'
import db from '@/lib/db'
export default async function handler(
req: NextApiRequest,
res: NextApiResponse
) {
if (req.method === 'GET') {
try {
const { rows } = await db.query('SELECT * FROM users ORDER BY created_at DESC')
res.status(200).json(rows)
} catch (error) {
res.status(500).json({ error: 'Failed to fetch users' })
}
} else if (req.method === 'POST') {
try {
const { name, email } = req.body
const { rows } = await db.query(
'INSERT INTO users (name, email) VALUES ($1, $2) RETURNING *',
[name, email]
)
res.status(201).json(rows[0])
} catch (error) {
res.status(500).json({ error: 'Failed to create user' })
}
} else {
res.setHeader('Allow', ['GET', 'POST'])
res.status(405).end(`Method ${req.method} Not Allowed`)
}
}
# Install Vercel CLI
npm install -g vercel
# Deploy
vercel
# For production deployment
vercel --prod
// vercel.json
{
"buildCommand": "npm run build",
"outputDirectory": ".next",
"framework": "nextjs",
"regions": ["iad1"],
"env": {
"NODE_ENV": "production"
}
}
# .env.local
NEXT_PUBLIC_API_URL=https://api.example.com
DATABASE_URL=postgresql://...
SECRET_KEY=your-secret-key
// components/OptimizedImage.tsx
import Image from 'next/image'
export default function OptimizedImage() {
return (
)
}
// components/LazyComponent.tsx
import dynamic from 'next/dynamic'
const LazyComponent = dynamic(() => import('./HeavyComponent'), {
loading: () => Loading...,
ssr: false,
})
export default function Page() {
return (
)
}
// app/layout.tsx
import { Analytics } from '@vercel/analytics/react'
export default function RootLayout({
children,
}: {
children: React.ReactNode
}) {
return (
{children}
)
}
# Dockerfile
FROM node:18-alpine AS base
# Install dependencies only when needed
FROM base AS deps
RUN apk add --no-cache libc6-compat
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci
# Rebuild the source code only when needed
FROM base AS builder
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY . .
RUN npm run build
# Production image, copy all the files and run next
FROM base AS runner
WORKDIR /app
ENV NODE_ENV production
RUN addgroup --system --gid 1001 nodejs
RUN adduser --system --uid 1001 nextjs
COPY --from=builder /app/public ./public
# Set the correct permission for prerender cache
RUN mkdir .next
RUN chown nextjs:nodejs .next
# Automatically leverage output traces to reduce image size
COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./
COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static
USER nextjs
EXPOSE 3000
ENV PORT 3000
CMD ["node", "server.js"]
# docker-compose.yml
version: '3.8'
services:
app:
build: .
ports:
- "3000:3000"
environment:
- NODE_ENV=production
- DATABASE_URL=postgresql://user:password@db:5432/mydb
depends_on:
- db
db:
image: postgres:13
environment:
- POSTGRES_DB=mydb
- POSTGRES_USER=user
- POSTGRES_PASSWORD=password
volumes:
- postgres_data:/var/lib/postgresql/data
volumes:
postgres_data:
// next.config.js
/** @type {import('next').NextConfig} */
const nextConfig = {
reactStrictMode: true,
swcMinify: true,
images: {
domains: ['example.com'],
formats: ['image/webp', 'image/avif'],
},
compiler: {
removeConsole: process.env.NODE_ENV === 'production',
},
experimental: {
optimizeCss: true,
},
async rewrites() {
return [
{
source: '/api/:path*',
destination: 'https://api.example.com/:path*',
},
]
},
async redirects() {
return [
{
source: '/home',
destination: '/',
permanent: true,
},
]
},
async headers() {
return [
{
source: '/(.*)',
headers: [
{
key: 'X-Content-Type-Options',
value: 'nosniff',
},
{
key: 'X-Frame-Options',
value: 'DENY',
},
{
key: 'X-XSS-Protection',
value: '1; mode=block',
},
],
},
]
},
}
module.exports = nextConfig