We use cookies to enhance your experience on the site
CodeWorlds

Edge Runtime and Performance Optimization

In the world of modern web applications, performance is a key factor affecting user experience and business success. Next.js offers advanced tools for performance optimization, among which Edge Runtime stands out as a powerful solution. In this module, we will discuss Edge Runtime and other performance optimization strategies in Next.js applications.

Edge Runtime in Next.js

What Is Edge Runtime?

Edge Runtime is a lightweight execution environment that enables running code closer to the end user - on the "edge" of the network - instead of in a central data center. Next.js enables running Route Handlers and pages in Edge Runtime, which provides:

  1. Lower latency - code executes closer to the user
  2. Faster Time to First Byte (TTFB) - faster response delivery
  3. Global scaling - automatic distribution worldwide
  4. Lower memory usage - thanks to the lighter runtime

Differences Between Edge Runtime and Node.js Runtime

It's worth understanding the differences between Edge Runtime and the traditional Node.js environment:

| Feature | Edge Runtime | Node.js Runtime | |---------|--------------|----------------| | Cold start time | Very fast (milliseconds) | Slower (hundreds of milliseconds) | | Scalability | Automatic, global | Requires configuration | | API access | Limited set | Full Node.js API | | Code size | Limited (typically 1-4 MB) | Practically unlimited | | File system access | None | Full access | | Function execution time | Limited (e.g., 50-60 ms) | Longer (minutes) | | Typical use cases | APIs, pages with dynamic content | Complex operations, database access |

Configuring Edge Runtime in Next.js

To run a page component or Route Handler in Edge Runtime, we use the

runtime
export in the file:

1// app/api/edge-route/route.ts
2import { NextRequest } from 'next/server';
3
4export const runtime = 'edge'; // Edge Runtime declaration
5
6export async function GET(request: NextRequest) {
7  const { searchParams } = new URL(request.url);
8  const query = searchParams.get('query') || 'World';
9
10  // Handling the request with minimal latency
11  return Response.json({ message: `Hello, ${query}!` });
12}

For a page or Layout component:

1// app/edge-page/page.tsx
2import { cookies } from 'next/headers';
3
4export const runtime = 'edge';
5
6export default async function EdgePage() {
7  const cookie = (await cookies()).get('theme')?.value || 'light';
8
9  return (
10    <div>
11      <h1>Page Rendered in Edge Runtime</h1>
12      <p>Your theme: {cookie}</p>
13    </div>
14  );
15}

When to Use Edge Runtime?

Edge Runtime is ideal for:

  1. Dynamic personalization - e.g., geolocation, user preferences
  2. API proxy - forwarding requests to other services with minimal latency
  3. A/B testing - quick decisions about which version to display
  4. Simple dynamic pages - that don't require complex operations
  5. International audiences - when network latency matters

Geolocation example in Edge Runtime:

1// app/edge-geo/page.tsx
2import { headers } from 'next/headers';
3
4export const runtime = 'edge';
5
6export default async function GeoPage() {
7  const headersList = await headers();
8  const countryCode = headersList.get('x-vercel-ip-country') || 'Unknown';
9  const city = headersList.get('x-vercel-ip-city') || 'Unknown City';
10
11  return (
12    <div>
13      <h1>Welcome, visitor!</h1>
14      <p>We detected you are from: {city}, {countryCode}</p>
15      {countryCode === 'PL' && (
16        <p>Special content for visitors from Poland!</p>
17      )}
18    </div>
19  );
20}

Image and Media Optimization

Next.js offers built-in solutions for media optimization that significantly impact application performance.

Next.js Image Component

The

Image
component in Next.js automatically optimizes images, reducing the size of transmitted data and improving Web Vitals metrics:

1import Image from 'next/image';
2
3export default function OptimizedImages() {
4  return (
5    <div>
6      <h2>Optimized Images in Next.js</h2>
7
8      <Image
9        src="/hero-image.jpg" // Relative path in the public directory
10        alt="Hero Image"
11        width={1200}
12        height={600}
13        priority // Loading priority for above-the-fold images
14      />
15
16      <Image
17        src="https://cdn.example.com/remote-image.jpg" // Remote image
18        alt="Remote Image"
19        width={800}
20        height={400}
21        quality={80} // Quality adjustment (1-100)
22        placeholder="blur"
23        blurDataURL="data:image/svg+xml;base64,..." // Optional placeholder while loading
24      />
25    </div>
26  );
27}

Key features of the

Image
component:

  1. Automatic responsive sizes - generating different image sizes
  2. Lazy loading - images loaded only when visible
  3. Conversion to modern formats - WebP, AVIF
  4. Quality optimization - file size reduction while maintaining quality
  5. Loading placeholder - blur effect preventing layout shifts (CLS)

Font Optimization

Next.js App Router introduces a new font loading system that eliminates text flickering issues (CLS):

1// app/layout.tsx
2import { Inter, Roboto_Mono } from 'next/font/google';
3
4// Variable font configuration
5const inter = Inter({
6  subsets: ['latin', 'latin-ext'],
7  display: 'swap',
8});
9
10// Fixed-weight font configuration
11const robotoMono = Roboto_Mono({
12  subsets: ['latin'],
13  display: 'swap',
14  weight: ['400', '700'],
15});
16
17export default function RootLayout({ children }) {
18  return (
19    <html lang="en" className={`${inter.className} ${robotoMono.variable}`}>
20      <body>{children}</body>
21    </html>
22  );
23}

In a component, we can use the defined font:

1// app/page.tsx
2export default function Home() {
3  return (
4    <main>
5      <h1 className={inter.className}>Heading in Inter Font</h1>
6      <code className="font-mono">// This will be in Roboto Mono thanks to the CSS variable</code>
7    </main>
8  );
9}

Data Optimization Strategies

React Server Components and Bundle Size Reduction

React Server Components (RSC) allow significantly reducing the amount of JavaScript sent to the browser:

1// app/products/page.tsx - Server Component (default)
2async function getProducts() {
3  const res = await fetch('https://api.example.com/products');
4  return res.json();
5}
6
7export default async function ProductsPage() {
8  // This code is NEVER sent to the client
9  const products = await getProducts();
10  const totalValue = products.reduce((sum, product) => sum + product.price, 0);
11
12  return (
13    <div>
14      <h1>Products (value: ${totalValue})</h1>
15      <ProductList products={products} />
16    </div>
17  );
18}

Streaming and Suspense

Streaming enables progressive UI delivery, which speeds up Time to First Byte and improves interactivity:

1// app/dashboard/page.tsx
2import { Suspense } from 'react';
3import Loading from './loading';
4
5// Components loading data independently from each other
6import UserProfile from './UserProfile';
7import RecentOrders from './RecentOrders';
8import Analytics from './Analytics';
9
10export default function Dashboard() {
11  return (
12    <div className="dashboard">
13      <h1>Control Panel</h1>
14
15      <div className="dashboard-grid">
16        {/* Faster-loading elements will be visible immediately */}
17        <Suspense fallback={<Loading section="profile" />}>
18          <UserProfile />
19        </Suspense>
20
21        <Suspense fallback={<Loading section="orders" />}>
22          <RecentOrders />
23        </Suspense>
24
25        <Suspense fallback={<Loading section="analytics" />}>
26          <Analytics />
27        </Suspense>
28      </div>
29    </div>
30  );
31}

Routing and Navigation Optimization

Prefetching and Parallel Routes

Next.js App Router automatically prefetches links visible in the viewport, which can be further controlled:

1import Link from 'next/link';
2
3export default function Navigation() {
4  return (
5    <nav>
6      {/* Default prefetch */}
7      <Link href="/products">Products</Link>
8
9      {/* Disabled prefetch */}
10      <Link href="/heavy-page" prefetch={false}>Heavy Page</Link>
11
12      {/* Prefetch headers only (for dynamic pages) */}
13      <Link href="/blog/article-123">Article</Link>
14    </nav>
15  );
16}

Parallel Routes enable loading multiple page sections independently from each other:

1// app/layout.tsx
2export default function Layout({ children, sidebar }) {
3  return (
4    <div className="layout">
5      <aside className="sidebar">
6        {sidebar}
7      </aside>
8      <main>
9        {children}
10      </main>
11    </div>
12  );
13}
14
15// app/@sidebar/page.tsx - loaded independently
16export default function Sidebar() {
17  return <div>Sidebar Content</div>;
18}
19
20// app/page.tsx - main content
21export default function Home() {
22  return <div>Main Content</div>;
23}

Route Handlers and API Optimization

Route Handlers can be optimized for performance, especially when combined with Edge Runtime:

1// app/api/optimized/route.ts
2import { NextResponse } from 'next/server';
3
4export const runtime = 'edge';
5
6export async function GET() {
7  // Applying CORS for preflight optimization
8  const response = NextResponse.json({ data: 'value' });
9
10  response.headers.set('Access-Control-Allow-Origin', '*');
11  response.headers.set('Access-Control-Allow-Methods', 'GET, OPTIONS');
12  response.headers.set('Access-Control-Allow-Headers', 'Content-Type');
13
14  // Setting Cache-Control for the browser
15  response.headers.set(
16    'Cache-Control',
17    'public, max-age=60, s-maxage=3600, stale-while-revalidate=600'
18  );
19
20  return response;
21}

Advanced Optimization Techniques

Best Practices for generateStaticParams

The

generateStaticParams
function is used to generate static versions of pages with parameters:

1// app/products/[category]/[id]/page.tsx
2export async function generateStaticParams() {
3  // Fetch only the necessary data for path generation
4  const categories = await fetch('https://api.example.com/categories').then(r => r.json());
5
6  // Generate parameter combinations for the most important pages
7  const paths = [];
8
9  for (const category of categories) {
10    // Fetch only top products for each category
11    const topProducts = await fetch(
12      `https://api.example.com/categories/${category.id}/top-products`
13    ).then(r => r.json());
14
15    for (const product of topProducts) {
16      paths.push({
17        category: category.slug,
18        id: product.id.toString(),
19      });
20    }
21  }
22
23  return paths;
24}
25
26export default async function ProductPage({ params }) {
27  const { category, id } = await params;
28  // Product page...
29}

Optimization involves:

  1. Generating only the most important parameters (e.g., most popular products)
  2. Minimizing the number of queries during building
  3. Properly caching data used for parameter generation

CSS and Style Optimization

Next.js supports various CSS loading strategies that can be optimized:

1// app/products/[id]/page.tsx
2import './product-page.css'; // Global CSS for this route
3
4// CSS Modules for the component
5import styles from './ProductDetails.module.css';
6
7// Styled JSX (built-in option)
8function ProductColorChoice() {
9  return (
10    <div className="color-selector">
11      <style jsx>{`
12        .color-selector {
13          display: flex;
14          gap: 8px;
15        }
16        .color-option {
17          width: 24px;
18          height: 24px;
19          border-radius: 50%;
20          cursor: pointer;
21        }
22      `}</style>
23      <div className="color-option" style={{ backgroundColor: 'red' }} />
24      <div className="color-option" style={{ backgroundColor: 'blue' }} />
25    </div>
26  );
27}
28
29export default async function ProductPage({ params }) {
30  return (
31    <div className={styles.productContainer}>
32      <h1 className={styles.productTitle}>Product Details {(await params).id}</h1>
33      <ProductColorChoice />
34    </div>
35  );
36}

CSS optimization:

  1. CSS Modules for component style isolation
  2. Automatic removal of unused CSS (tree-shaking)
  3. Grouping CSS into chunks according to application routes

Leveraging HTTP/2 and HTTP/3

Hosting Next.js on platforms supporting modern HTTP/2 and HTTP/3 protocols provides better performance through:

  1. Request multiplexing - multiple requests through a single connection
  2. Header compression - lower communication overhead
  3. Server Push - the server can send resources before the client requests them

This doesn't require code changes, just appropriate server configuration or choosing a hosting platform (e.g., Vercel, Netlify, Cloudflare Pages).

Performance Analysis and Improvement Tools

Next.js Analytics and Web Vitals

Next.js offers built-in Web Vitals metrics that we can track:

1// instrumentation.ts (in the root directory)
2export function register() {
3  if (typeof window !== 'undefined') {
4    // This solution works only on the client side
5    import('web-vitals').then(({ onCLS, onFID, onLCP, onTTFB }) => {
6      onCLS(metric => sendAnalyticsEvent('CLS', metric));
7      onFID(metric => sendAnalyticsEvent('FID', metric));
8      onLCP(metric => sendAnalyticsEvent('LCP', metric));
9      onTTFB(metric => sendAnalyticsEvent('TTFB', metric));
10    });
11  }
12}
13
14function sendAnalyticsEvent(name, metric) {
15  const body = JSON.stringify({
16    name,
17    value: metric.value,
18    page: window.location.pathname,
19    id: metric.id,
20  });
21
22  // Send to your own API
23  navigator.sendBeacon('/api/analytics', body);
24}

Bundle Size Audit and Analysis

Bundle size analysis is critical for performance. Next.js offers built-in bundle analysis:

1next build --analyze

We can also use more advanced tools like

@next/bundle-analyzer
:

1// next.config.js
2const withBundleAnalyzer = require('@next/bundle-analyzer')({
3  enabled: process.env.ANALYZE === 'true',
4});
5
6module.exports = withBundleAnalyzer({
7  // Next.js configuration
8});

Then we run:

1ANALYZE=true npm run build

Security Best Practices in Next.js

In the Quantum Metropolis of 2150, security is not optional - it's a necessity. Every vulnerability in the system can be exploited by cybercriminals or hostile corporations. In this section, we will discuss the most important practices for securing Next.js applications that protect both user data and the integrity of the entire system.

CSRF Protection (Cross-Site Request Forgery)

CSRF is an attack where a malicious website uses an authenticated user's session to perform unauthorized actions. Next.js offers built-in protection mechanisms, especially in Server Actions.

Implementing CSRF Protection

1// middleware.ts
2import { NextResponse } from 'next/server';
3import type { NextRequest } from 'next/server';
4
5export function middleware(request: NextRequest) {
6  // Verify the Origin header for mutations
7  if (request.method !== 'GET' && request.method !== 'HEAD') {
8    const origin = request.headers.get('origin');
9    const host = request.headers.get('host');
10
11    // Check if Origin matches the host
12    if (origin && !origin.includes(host || '')) {
13      return new NextResponse('Forbidden', { status: 403 });
14    }
15  }
16
17  return NextResponse.next();
18}
19
20export const config = {
21  matcher: '/api/:path*'
22};

CSRF Token for Forms

1// lib/csrf.ts
2import { cookies } from 'next/headers';
3import crypto from 'crypto';
4
5export async function generateCSRFToken(): Promise<string> {
6  const token = crypto.randomBytes(32).toString('hex');
7  const cookieStore = await cookies();
8
9  cookieStore.set('csrf-token', token, {
10    httpOnly: true,
11    secure: process.env.NODE_ENV === 'production',
12    sameSite: 'strict',
13    maxAge: 60 * 60 // 1 hour
14  });
15
16  return token;
17}
18
19export async function verifyCSRFToken(token: string): Promise<boolean> {
20  const cookieStore = await cookies();
21  const storedToken = cookieStore.get('csrf-token')?.value;
22
23  return storedToken === token;
24}
25
26// app/actions.ts
27'use server';
28
29import { verifyCSRFToken } from '@/lib/csrf';
30
31export async function submitForm(formData: FormData) {
32  const csrfToken = formData.get('csrf-token') as string;
33
34  if (!await verifyCSRFToken(csrfToken)) {
35    throw new Error('Invalid CSRF token');
36  }
37
38  // Process the form...
39}
40
41// app/my-form/page.tsx
42import { generateCSRFToken } from '@/lib/csrf';
43import { submitForm } from '@/app/actions';
44
45export default async function MyFormPage() {
46  const csrfToken = await generateCSRFToken();
47
48  return (
49    <form action={submitForm}>
50      <input type="hidden" name="csrf-token" value={csrfToken} />
51      {/* Remaining form fields */}
52      <button type="submit">Submit</button>
53    </form>
54  );
55}

XSS Prevention (Cross-Site Scripting)

XSS is an attack where an attacker injects malicious JavaScript code into the application. Next.js and React offer built-in protection, but it's worth knowing additional techniques.

Default React Protection

React automatically escapes all values rendered in JSX:

1// Safe - React automatically escapes
2function UserProfile({ username }: { username: string }) {
3  // Even if username = "<script>alert('xss')</script>"
4  // React will display it as text, not execute it
5  return <div>Welcome, {username}!</div>;
6}

Be Careful with dangerouslySetInnerHTML

1// DANGEROUS - can execute malicious code
2function DangerousComponent({ html }: { html: string }) {
3  return <div dangerouslySetInnerHTML={{ __html: html }} />;
4}
5
6// SAFE - use a sanitization library
7import DOMPurify from 'isomorphic-dompurify';
8
9function SafeComponent({ html }: { html: string }) {
10  const sanitized = DOMPurify.sanitize(html, {
11    ALLOWED_TAGS: ['p', 'strong', 'em', 'a', 'ul', 'ol', 'li'],
12    ALLOWED_ATTR: ['href', 'target']
13  });
14
15  return <div dangerouslySetInnerHTML={{ __html: sanitized }} />;
16}

Content Security Policy (CSP)

CSP is a security mechanism against XSS that specifies which code sources are trusted:

1// next.config.js
2const ContentSecurityPolicy = `
3  default-src 'self';
4  script-src 'self' 'unsafe-eval' 'unsafe-inline' https://trusted-cdn.com;
5  style-src 'self' 'unsafe-inline' https://fonts.googleapis.com;
6  img-src 'self' data: https: blob:;
7  font-src 'self' https://fonts.gstatic.com;
8  connect-src 'self' https://api.example.com;
9  frame-ancestors 'none';
10`;
11
12const securityHeaders = [
13  {
14    key: 'Content-Security-Policy',
15    value: ContentSecurityPolicy.replace(/\s{2,}/g, ' ').trim()
16  }
17];
18
19module.exports = {
20  async headers() {
21    return [
22      {
23        source: '/:path*',
24        headers: securityHeaders
25      }
26    ];
27  }
28};

Or via middleware:

1// middleware.ts
2import { NextResponse } from 'next/server';
3import type { NextRequest } from 'next/server';
4
5export function middleware(request: NextRequest) {
6  const response = NextResponse.next();
7
8  const csp = [
9    "default-src 'self'",
10    "script-src 'self' 'unsafe-eval' 'unsafe-inline'",
11    "style-src 'self' 'unsafe-inline'",
12    "img-src 'self' data: https:",
13    "font-src 'self' data:",
14    "connect-src 'self' https://api.example.com"
15  ].join('; ');
16
17  response.headers.set('Content-Security-Policy', csp);
18
19  return response;
20}

Rate Limiting - Protection Against Abuse

Rate limiting protects the application against DDoS attacks and API abuse. We can implement this in Next.js Middleware:

1// lib/rate-limit.ts
2import { LRUCache } from 'lru-cache';
3
4type RateLimitOptions = {
5  interval: number; // time in milliseconds
6  uniqueTokenPerInterval: number; // maximum number of unique tokens
7};
8
9export function rateLimit(options: RateLimitOptions) {
10  const tokenCache = new LRUCache({
11    max: options.uniqueTokenPerInterval || 500,
12    ttl: options.interval || 60000
13  });
14
15  return {
16    check: (limit: number, token: string): { success: boolean; remaining: number } => {
17      const tokenCount = (tokenCache.get(token) as number[]) || [0];
18
19      if (tokenCount[0] === 0) {
20        tokenCache.set(token, [1]);
21        return { success: true, remaining: limit - 1 };
22      }
23
24      const currentCount = tokenCount[0];
25
26      if (currentCount >= limit) {
27        return { success: false, remaining: 0 };
28      }
29
30      tokenCache.set(token, [currentCount + 1]);
31      return { success: true, remaining: limit - currentCount - 1 };
32    }
33  };
34}
35
36// middleware.ts
37import { NextResponse } from 'next/server';
38import type { NextRequest } from 'next/server';
39import { rateLimit } from '@/lib/rate-limit';
40
41const limiter = rateLimit({
42  interval: 60 * 1000, // 1 minute
43  uniqueTokenPerInterval: 500
44});
45
46export async function middleware(request: NextRequest) {
47  // Use IP as identifier
48  const ip = request.ip ?? 'anonymous';
49
50  try {
51    const { success, remaining } = limiter.check(10, ip); // 10 requests per minute
52
53    if (!success) {
54      return new NextResponse('Too Many Requests', {
55        status: 429,
56        headers: {
57          'X-RateLimit-Limit': '10',
58          'X-RateLimit-Remaining': '0',
59          'X-RateLimit-Reset': new Date(Date.now() + 60000).toISOString()
60        }
61      });
62    }
63
64    const response = NextResponse.next();
65    response.headers.set('X-RateLimit-Limit', '10');
66    response.headers.set('X-RateLimit-Remaining', remaining.toString());
67
68    return response;
69  } catch (error) {
70    return new NextResponse('Internal Server Error', { status: 500 });
71  }
72}
73
74export const config = {
75  matcher: '/api/:path*'
76};

Rate Limiting for Server Actions

1// lib/rate-limit-server-action.ts
2import { cookies } from 'next/headers';
3
4const rateLimitStore = new Map<string, { count: number; resetAt: number }>();
5
6export async function checkRateLimit(
7  action: string,
8  limit: number = 10,
9  windowMs: number = 60000
10): Promise<boolean> {
11  const cookieStore = await cookies();
12  const sessionId = cookieStore.get('session-id')?.value || 'anonymous';
13  const key = `${sessionId}:${action}`;
14
15  const now = Date.now();
16  const record = rateLimitStore.get(key);
17
18  if (!record || now > record.resetAt) {
19    rateLimitStore.set(key, {
20      count: 1,
21      resetAt: now + windowMs
22    });
23    return true;
24  }
25
26  if (record.count >= limit) {
27    return false;
28  }
29
30  record.count++;
31  return true;
32}
33
34// app/actions.ts
35'use server';
36
37import { checkRateLimit } from '@/lib/rate-limit-server-action';
38
39export async function sensitiveAction(data: FormData) {
40  const allowed = await checkRateLimit('sensitive-action', 5, 60000);
41
42  if (!allowed) {
43    throw new Error('Rate limit exceeded. Please try again later.');
44  }
45
46  // Execute the action...
47}

Secure Headers (HSTS, X-Frame-Options, etc.)

Proper HTTP headers are the foundation of web application security:

1// next.config.js
2const securityHeaders = [
3  {
4    key: 'X-DNS-Prefetch-Control',
5    value: 'on'
6  },
7  {
8    key: 'Strict-Transport-Security',
9    value: 'max-age=63072000; includeSubDomains; preload'
10  },
11  {
12    key: 'X-Frame-Options',
13    value: 'SAMEORIGIN'
14  },
15  {
16    key: 'X-Content-Type-Options',
17    value: 'nosniff'
18  },
19  {
20    key: 'X-XSS-Protection',
21    value: '1; mode=block'
22  },
23  {
24    key: 'Referrer-Policy',
25    value: 'strict-origin-when-cross-origin'
26  },
27  {
28    key: 'Permissions-Policy',
29    value: 'camera=(), microphone=(), geolocation=()'
30  }
31];
32
33module.exports = {
34  async headers() {
35    return [
36      {
37        source: '/:path*',
38        headers: securityHeaders
39      }
40    ];
41  }
42};

Or via middleware for greater control:

1// middleware.ts
2import { NextResponse } from 'next/server';
3import type { NextRequest } from 'next/server';
4
5export function middleware(request: NextRequest) {
6  const response = NextResponse.next();
7
8  // HSTS - enforces HTTPS
9  response.headers.set(
10    'Strict-Transport-Security',
11    'max-age=31536000; includeSubDomains'
12  );
13
14  // X-Frame-Options - protection against clickjacking
15  response.headers.set('X-Frame-Options', 'SAMEORIGIN');
16
17  // X-Content-Type-Options - prevents MIME sniffing
18  response.headers.set('X-Content-Type-Options', 'nosniff');
19
20  // X-XSS-Protection - additional XSS protection
21  response.headers.set('X-XSS-Protection', '1; mode=block');
22
23  // Referrer-Policy - controls information in the Referer header
24  response.headers.set('Referrer-Policy', 'strict-origin-when-cross-origin');
25
26  // Permissions-Policy - controls access to browser APIs
27  response.headers.set(
28    'Permissions-Policy',
29    'camera=(), microphone=(), geolocation=(), payment=()'
30  );
31
32  return response;
33}

API Routes and Server Actions Security

Input Validation in Server Actions

1// app/actions.ts
2'use server';
3
4import { z } from 'zod';
5import { revalidatePath } from 'next/cache';
6
7const CreatePostSchema = z.object({
8  title: z.string().min(3).max(100),
9  content: z.string().min(10).max(5000),
10  tags: z.array(z.string()).max(5).optional()
11});
12
13export async function createPost(formData: FormData) {
14  // Input data validation
15  const rawData = {
16    title: formData.get('title'),
17    content: formData.get('content'),
18    tags: formData.getAll('tags')
19  };
20
21  const validation = CreatePostSchema.safeParse(rawData);
22
23  if (!validation.success) {
24    return {
25      error: 'Validation failed',
26      issues: validation.error.issues
27    };
28  }
29
30  const { title, content, tags } = validation.data;
31
32  // Authentication verification
33  const session = await getServerSession();
34  if (!session?.user) {
35    return { error: 'Unauthorized' };
36  }
37
38  // HTML sanitization (if we allow formatting)
39  const sanitizedContent = sanitizeHtml(content);
40
41  try {
42    const post = await db.post.create({
43      data: {
44        title,
45        content: sanitizedContent,
46        tags,
47        authorId: session.user.id
48      }
49    });
50
51    revalidatePath('/blog');
52
53    return { success: true, post };
54  } catch (error) {
55    console.error('Failed to create post:', error);
56    return { error: 'Failed to create post' };
57  }
58}

Secure API Routes

1// app/api/users/route.ts
2import { NextRequest, NextResponse } from 'next/server';
3import { getServerSession } from 'next-auth';
4import { z } from 'zod';
5
6const QuerySchema = z.object({
7  page: z.coerce.number().min(1).default(1),
8  limit: z.coerce.number().min(1).max(100).default(10)
9});
10
11export async function GET(request: NextRequest) {
12  try {
13    // Authentication verification
14    const session = await getServerSession();
15    if (!session) {
16      return NextResponse.json(
17        { error: 'Unauthorized' },
18        { status: 401 }
19      );
20    }
21
22    // Query parameter validation
23    const { searchParams } = new URL(request.url);
24    const validation = QuerySchema.safeParse({
25      page: searchParams.get('page'),
26      limit: searchParams.get('limit')
27    });
28
29    if (!validation.success) {
30      return NextResponse.json(
31        { error: 'Invalid parameters', issues: validation.error.issues },
32        { status: 400 }
33      );
34    }
35
36    const { page, limit } = validation.data;
37
38    // Secure database query
39    const users = await db.user.findMany({
40      skip: (page - 1) * limit,
41      take: limit,
42      select: {
43        id: true,
44        name: true,
45        email: true,
46        // DO NOT return passwords or other sensitive data
47      }
48    });
49
50    return NextResponse.json({ users });
51  } catch (error) {
52    console.error('API Error:', error);
53    // DO NOT return error details in production
54    return NextResponse.json(
55      { error: 'Internal server error' },
56      { status: 500 }
57    );
58  }
59}

Additional Best Practices

1. Secure Secret Storage

1// .env.local (NEVER commit to repo!)
2DATABASE_URL="postgresql://..."
3NEXTAUTH_SECRET="super-secret-key"
4API_KEY="secret-api-key"
5
6// app/config.ts
7const requiredEnvVars = [
8  'DATABASE_URL',
9  'NEXTAUTH_SECRET',
10  'API_KEY'
11] as const;
12
13// Validate the presence of environment variables at startup
14requiredEnvVars.forEach((varName) => {
15  if (!process.env[varName]) {
16    throw new Error(`Missing required environment variable: ${varName}`);
17  }
18});
19
20export const config = {
21  database: {
22    url: process.env.DATABASE_URL!
23  },
24  auth: {
25    secret: process.env.NEXTAUTH_SECRET!
26  },
27  api: {
28    key: process.env.API_KEY!
29  }
30} as const;

2. Secure Cookies

1// lib/cookies.ts
2import { cookies } from 'next/headers';
3
4export async function setSecureCookie(name: string, value: string) {
5  const cookieStore = await cookies();
6
7  cookieStore.set(name, value, {
8    httpOnly: true, // Not accessible to JavaScript
9    secure: process.env.NODE_ENV === 'production', // HTTPS only in production
10    sameSite: 'strict', // CSRF protection
11    maxAge: 60 * 60 * 24 * 7, // 7 days
12    path: '/'
13  });
14}

3. Logging and Monitoring

1// lib/logger.ts
2export function logSecurityEvent(event: {
3  type: 'auth_failure' | 'rate_limit' | 'csrf_violation' | 'xss_attempt';
4  ip: string;
5  userAgent: string;
6  details?: any;
7}) {
8  const logEntry = {
9    timestamp: new Date().toISOString(),
10    ...event
11  };
12
13  // In production, send to a monitoring system (Sentry, LogRocket, etc.)
14  if (process.env.NODE_ENV === 'production') {
15    // sendToMonitoring(logEntry);
16  }
17
18  console.warn('Security Event:', logEntry);
19}
20
21// middleware.ts
22export function middleware(request: NextRequest) {
23  const ip = request.ip ?? 'unknown';
24  const userAgent = request.headers.get('user-agent') ?? 'unknown';
25
26  // Log suspicious activities
27  if (isSuspicious(request)) {
28    logSecurityEvent({
29      type: 'csrf_violation',
30      ip,
31      userAgent,
32      details: { url: request.url }
33    });
34  }
35
36  return NextResponse.next();
37}

Security Best Practices Summary

In the Quantum Metropolis, security is a continuous process, not a one-time action. Key principles:

  1. Never trust user input - always validate and sanitize
  2. Use HTTPS everywhere - enforce through HSTS
  3. Implement rate limiting - protect against abuse
  4. Set secure headers - CSP, X-Frame-Options, etc.
  5. Protect against CSRF - verify Origin, use tokens
  6. Prevent XSS - sanitize HTML, use CSP
  7. Store secrets securely - use environment variables
  8. Log security events - monitor suspicious activities
  9. Regularly update dependencies - patch known vulnerabilities
  10. Apply the principle of least privilege - minimal permissions

Summary: Performance Optimization Best Practices

In summary, here are the key principles for performance optimization in Next.js:

  1. Use Edge Runtime for dynamic, global applications requiring low latency
  2. Leverage React Server Components to reduce the amount of JS sent to the client
  3. Optimize images using the
    Image
    component from Next.js
  4. Implement streaming with the
    Suspense
    component for better UI responsiveness
  5. Manage caching at different levels (browser, CDN, server)
  6. Leverage prefetching for faster navigation between pages
  7. Monitor Web Vitals and continuously optimize critical rendering paths
  8. Optimize font loading using Next.js's built-in font system
  9. Eliminate unnecessary JavaScript through bundle size analysis and removing unused dependencies
  10. Test on real devices and slower connections to identify performance issues

By applying these practices, you can build Next.js applications that are both functional and blazingly fast, providing users with excellent experiences regardless of their location or device.

Go to CodeWorlds