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 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:
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 |
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}Edge Runtime is ideal for:
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}Next.js offers built-in solutions for media optimization that significantly impact application performance.
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: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}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 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}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 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}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:
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:
Hosting Next.js on platforms supporting modern HTTP/2 and HTTP/3 protocols provides better performance through:
This doesn't require code changes, just appropriate server configuration or choosing a hosting platform (e.g., Vercel, Netlify, Cloudflare Pages).
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 analysis is critical for performance. Next.js offers built-in bundle analysis:
1next build --analyzeWe 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 buildIn 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 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.
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};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 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.
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}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}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 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};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}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}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}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}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;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}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}In the Quantum Metropolis, security is a continuous process, not a one-time action. Key principles:
In summary, here are the key principles for performance optimization in Next.js:
Image component from Next.jsSuspense component for better UI responsivenessBy 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.