From Development to Deployment: Next.js Full-Stack Best Practices on EdgeOne Pages

This guide focuses on building high-performance Next.js full-stack applications and optimizing your development workflow without compromising code quality or application speed.
In the upcoming sections, we'll thoroughly examine project architecture, environment configuration, routing strategies, and efficient data fetching techniques. These best practices will enable you to deploy Next.js full-stack applications seamlessly on EdgeOne Pages, harnessing edge computing power for superior performance and worldwide accessibility.
Directory Structure Recommendations
The following directory structure represents our recommended Next.js hybrid rendering template. This carefully organized architecture serves as an optimal foundation for developers building scalable full-stack applications:
next-mix-template/
├── src/
│ ├── app/ # Next.js App Router
│ │ ├── layout.tsx
│ │ ├── page.tsx # Home page component
│ │ ├── ssr/ # SSR demonstration pages
│ │ ├── isr/ # ISR demonstration pages
│ │ ├── ssg/ # SSG demonstration pages
│ │ ├── streaming/ # Streaming demonstration pages
│ │ ├── node-functions/# Node Functions demonstration pages
│ │ ├── edge-functions/# Edge Functions demonstration pages
│ │ ├── api/ # API routes
│ │ └── globals.css
│ ├── components/ # React components
│ │ ├── ui/
│ │ ├── Header.tsx
│ │ ├── Hero.tsx
│ │ ├── Features.tsx
│ │ └── FeatureCard.tsx
│ └── lib/ # Utility functions
├── public/ # Static resources
├── package.json # Project configuration
├── next.config.ts # Next.js configuration
├── tailwind.config.ts
├── tsconfig.json
└── components.json # shadcn/ui configuration
This project follows the standard Next.js App Router architecture with two specialized backend function directories that integrate seamlessly with EdgeOne Pages:
The edge-functions
directory serves as the entry point for EdgeOne Pages' edge computing capabilities. Place lightweight requests here to benefit from rapid response times as they execute directly from edge nodes closest to your users, delivering exceptional speed and performance.
EdgeOne Pages provides node-functions
, a centralized function deployment environment where you can leverage the extensive Node.js package ecosystem. This enables you to quickly implement more complex functionality requiring full Node.js capabilities while maintaining a streamlined development experience.
For more comprehensive details about edge-functions and node-functions, please refer to the official documentation.
Routes and API Integration
EdgeOne Pages enables direct access to pages and API endpoints via domain-based path routing. The mapping follows a consistent pattern where file locations directly correspond to URL paths - for instance, /app/
node-functions
/get-users/
index.js
becomes accessible at {domain}/get-users
.
This platform features an intuitive routing system with automatic path mapping:
Key Features:
- Automatic Path Mapping: File system structure directly translates to URL routes
- Zero Configuration: No manual routing setup required
- Unified Route Management: Seamless handling of both page and API routes
- Port-Free Access: Direct domain-based routing eliminates port specifications
Project structure:
/app/node-functions/get-users/index.js
/app/node-functions/create-user/index.js
/app/node-functions/products/[id].js
/app/edge-functions/get-products/index.js (edge)
Access method:
https://yourdomain.com/get-users → run get-users/index.js
https://yourdomain.com/create-user → run create-user/index.js
https://yourdomain.com/products/123 → run products/[id].js
https://yourdomain.com/get-products → run get-products/index.js (edge)
Page Rendering & Data Fetching
Next.js offers multiple rendering strategies, each with distinct data fetching mechanisms. EdgeOne Pages provides comprehensive support for Next.js's three primary rendering modes - Static Site Generation (SSG), Server-Side Rendering (SSR), and Incremental Static Regeneration (ISR) - enabling developers to leverage all approaches within the same application.
1. App Router Data Fetching (Next.js 13+)
The new App Router employs different approaches for data fetching. For comprehensive details, consult the official Next.js documentation.
// app/products/page.js
// Default static rendering
async function getProducts() {
const res = await fetch('https://api.example.com/products', {
cache: 'force-cache' // Default Cache
})
return res.json()
}
export default async function ProductsPage() {
const products = await getProducts()
return (
<div>
{products.map(product => (
<div key={product.id}>{product.name}</div>
))}
</div>
)
}
Setting revalidate: 0
forces pages to use server-side rendering for data retrieval on every request.
// app/live/page.js
// Dynamic rendering (similar to getServerSideProps)
async function getLiveData() {
const res = await fetch('https://api.example.com/live', {
cache: 'no-store', // No caching, get new data for each request
// 或使用
next: { revalidate: 0 }
})
return res.json()
}
// ISR in App Router
async function getDataWithISR() {
const res = await fetch('https://api.example.com/data', {
next: { revalidate: 60 } // Re-verify after 60 seconds
})
return res.json()
}
// pages/dashboard.js
// Combining static and client-side data fetching
export async function getStaticProps() {
// Get data that doesn't change often
const staticData = await fetch('https://api.example.com/config')
const config = await staticData.json()
return {
props: { config },
revalidate: 3600, // 1 hour
}
}
function Dashboard({ config }) {
// Client obtains real-time data
const { data: liveData } = useSWR('/api/live-stats', fetcher, {
refreshInterval: 5000
})
return (
<div>
<h1>{config.title}</h1>
{liveData && <div>online users: {liveData.onlineUsers}</div>}
</div>
)
}
2. getStaticProps (Static Generation)
Fetches data during the build process and generates static HTML pages. Ideal for content that remains relatively stable and doesn't require frequent updates.
// pages/products.js
export async function getStaticProps() {
const res = await fetch('https://api.example.com/products')
const products = await res.json()
return {
props: {
products,
}
}
}
export default function Products({ products }) {
return (
<div>
{products.map(product => (
<div key={product.id}>{product.name}</div>
))}
</div>
)
}
Use Case: Next.js Official Website (nextjs.org)
3. getServerSideProps (Server-Side Rendering)
Server-Side Rendering (SSR) retrieves data server-side on every request. This method is ideal for pages requiring real-time data updates or access to request-specific information.
// pages/user/[id].js
export async function getServerSideProps(context) {
const { id } = context.params
const { req, res, query } = context
const cookies = context.req.cookies
const userData = await fetch(`https://api.example.com/users/${id}`)
const user = await userData.json()
if (!user) {
return {
notFound: true,
}
}
return {
props: {
user,
}
}
}
export default function User({ user }) {
return <div>Welcome, {user.name}!</div>
}
Use Case: LinkedIn (linkedin.com)
4. ISR (Incremental Static Regeneration)
The revalidate
parameter enables Incremental Static Regeneration (ISR), providing rule-based, on-demand page data updates.
export async function getStaticProps() {
const res = await fetch('https://api.example.com/blog')
const posts = await res.json()
return {
props: {
posts,
},
// Three ISR strategies
revalidate: 60, // Regenerate after a time interval in seconds
// or
revalidate: false, // Disable ISR and only generate it at build time
// or
revalidate: true, // On-demand ISR (configuration required)
}
}
Use Case: Dev.to (dev.to). Popular content updates every 5 minutes
Method Comparison and Decision Matrix
Method | Execution Time | Use Case | SEO | Performance |
getStaticProps | Build time | Static content | ✅ Excellent | ⚡ Fastest |
getServerSideProps | Every request | Dynamic content/Personalization | ✅ Good | 🐢 Slower |
ISR (revalidate) | Build time + Periodic updates | Semi-static content | ✅ Good | ⚡ Fast |
App Router fetch | Depends on configuration | Flexible | ✅ Good | 🔧 Configurable |
Developers use a decision matrix:
Website Type | Recommended Strategy | Representative Sites | Key Considerations |
Documentation/Blog | SSG | Next.js Docs, MDN | SEO, Performance, Cost |
E-commerce Platform | SSR/ISR | Amazon, Shopify | Real-time Inventory, Personalization |
Social Media | SSR | Twitter, LinkedIn | Real-time Updates, Personalization |
News Media | ISR/SSR | CNN, BBC | Timeliness, SEO |
SaaS Marketing Site | SSG/ISR | Stripe, Slack | Performance, Conversion Rate |
Corporate Website | SSG | Apple, Microsoft | Brand Presentation, Performance |
Online Education | ISR | Coursera, Udemy | Content Updates, SEO |
Streaming Media | SSR | Netflix, Spotify | Personalization, Real-time Recommendations |
Environment Variables Management
EdgeOne Pages adheres to Next.js conventions for environment variable management:
- Public Variables : Environment variables prefixed with `NEXT_PUBLIC_` will be embedded in the client-side bundle and accessible in browser code. These variables should only contain non-sensitive information.
- Protected Variables: For sensitive information such as API keys, authentication tokens (e.g., `AI_TOKEN`), or other credentials, omit the `NEXT_PUBLIC_` prefix. These variables will remain server-side only, protecting them from exposure in the client-side code.
This consistent approach allows you to manage visibility and security of configuration values while maintaining compatibility with the standard Next.js environment variable system.
# private env
OPENAI_API_URL=
OPENAI_API_KEY=
DB_HOST=
DB_PORT=
DB_USER=
DB_PASSWORD=
DB_NAME=
# public env
NEXT_PUBLIC_AUTHOR=
NEXT_PUBLIC_EMAIL=
Usually, a single .env.local file is sufficient. However, sometimes you may want to add some default settings separately for the development (next dev) or production (next start) environments.
Next.js allows you to set default values in .env (for all environments), .env.development (for development environment), and .env.production (for production environment)..env.local always overrides the default settings.
When deploying to EdgeOne Pages, you'll need to configure environment variables in the EdgeOne console that match those in your local .
env
files. EdgeOne Pages follows Next.js conventions where variables prefixed with NEXT_PUBLIC_
are embedded in client-side code, while variables without this prefix remain protected server-side.
Note: For security, always add
.
env
*
files to your.gitignore
to prevent sensitive credentials like API keys and tokens from being accidentally committed to your repository.
Conclude
EdgeOne Pages provides comprehensive native support for Next.js full-stack projects, perfectly compatible with various core Next.js features including Static Site Generation (SSG), Server-Side Rendering (SSR), Incremental Static Regeneration (ISR), and API Routes. Developers can leverage all the advantages of the Next.js framework without complex configuration adjustments, enabling flexible rendering strategy selection and an efficient full-stack development experience.
Thanks to EdgeOne Pages' streamlined deployment process, development teams can quickly deploy Next.js applications to the global edge network through EdgeOne CLI operations or CI/CD integration. This seamless deployment experience not only significantly reduces operational complexity but also delivers exceptional performance and reliability through edge computing capabilities, allowing developers to focus more on implementing business logic without worrying about infrastructure management and optimization.