Follow these best practices to build maintainable, performant, and secure e-commerce applications.
1. Environment Variables
Always use environment variables for sensitive data:
// ❌ Bad - Hardcoded credentials
const url = "https://store.com"; // Hardcoded url
const key = "ck_1234567890abcdef"; // Hardcoded key
const secret = "cs_1234567890abcdef"; // Hardcoded secret
// ✅ Good - Environment variables
const url = process.env.WOO_BASE_URL; // Environment variable url
const key = process.env.WOO_CONSUMER_KEY; // Environment variable key
const secret = process.env.WOO_CONSUMER_SECRET; // Environment variable secret
// Always validate
if (!url || !key || !secret) { // If url, key or secret is not provided, throw an error
throw new Error("Missing required environment variables"); // Throw error with the given error message
}Security: Never commit .env.local to version control. It’s already in .gitignore.
2. Error Handling
Always handle errors in API calls and user operations:
try {
const response = await fetch("/api/products"); // Fetch products with the given api route
if (!response.ok) {
const error = await response.json(); // Get error from response
throw new Error(error.error || "Failed to fetch products"); // Throw error with the given error message
}
const data = await response.json();
// Use data // Use data with the given data
} catch (error) {
console.error("Error:", error); // Log error with the given error
// Show user-friendly error message // Show user-friendly error message with the given error message
setError(error.message || "An error occurred"); // Set error with the given error message
}3. Loading States
Show loading indicators during async operations:
"use client";
import { useState, useEffect } from "react";
export default function ProductsList() { // Products list component
const [products, setProducts] = useState([]); // Set products with the given products
const [loading, setLoading] = useState(true); // Set loading with the given loading
const [error, setError] = useState(""); // Set error with the given error
useEffect(() => {
async function fetchProducts() {
try {
setLoading(true);
const response = await fetch("/api/products"); // Fetch products with the given api route
const data = await response.json(); // Get data from response
setProducts(data); // Set products with the given data
} catch (error) {
setError(error.message); // Set error with the given error message
} finally {
setLoading(false); // Set loading with the given loading
}
}
fetchProducts();
}, []);
if (loading) return ; // Return loading spinner if loading is true
if (error) return ; // Return error message if error is true
return (
{products.map(product => ( // Display products with the given product id and product
))} // Display product card with the given product id and product
);
}4. Optimistic Updates
Update UI immediately, sync with server in background:
const addToCart = async (productId) => {
// Save previous state // Save previous state with the given previous cart
const previousCart = cartData; // Set previous cart with the given cart data
// Update UI immediately (optimistic)
setCartData({ // Set cart data with the given cart data
...cartData, // Spread cart data with the given cart data
items: [...cartData.items, { id: productId, quantity: 1 }] // Add product id and quantity 1 to the items with the given items
}); // Set cart data with the given cart data
try {
// Sync with server
await fetch("/api/cart", { // Fetch cart with the given api route
method: "POST",
body: JSON.stringify({ id: productId, quantity: 1 }) // Add product id and quantity 1 to the body with the given body
}); // Fetch cart with the given api route
} catch (error) {
// Revert on error
setCartData(previousCart); // Set cart data with the given previous cart
throw error; // Throw error with the given error
}
};5. Image Optimization
Use Next.js Image component for optimized images:
import Image from "next/image";
export default function ProductImage({ product }) { // Product image component with the given product
return (
);
}6. SEO Optimization
Add metadata to pages for better SEO:
export async function generateMetadata({ params }) {
const product = await WooCommerceAPI.getProductBySlug(params.slug); // Get product with the given slug
return {
title: product.name, // Set title with the given name
description: product.short_description, // Set description with the given short description
openGraph: {
title: product.name, // Set title with the given name
description: product.short_description, // Set description with the given short description
images: [product.images[0]?.src], // Set images with the given images
type: "website", // Set type with the given type
},
twitter: {
card: "summary_large_image", // Set card with the given card
title: product.name, // Set title with the given name
description: product.short_description,
}, // Set description with the given short description
}; // Return metadata with the given metadata
}7. Code Splitting
Use dynamic imports for heavy components:
import dynamic from "next/dynamic";
// Lazy load heavy components
const HeavyComponent = dynamic(() => import("./HeavyComponent"), { // Import HeavyComponent component with the given HeavyComponent component
loading: () => , // Set loading with the given loading spinner
ssr: false // If component uses browser-only APIs
}); // Import HeavyComponent component with the given HeavyComponent component and loading spinner and ssr false
// Use in your page
export default function Page() { // Page component
return (
// Display HeavyComponent component
);
}8. API Request Caching
Cache API responses to reduce requests:
// Cache products for 5 minutes
const cache = new Map(); // Set cache with the given cache
async function getCachedProducts() { // Get cached products function
const cacheKey = "products"; // Set cache key with the given cache key
const cached = cache.get(cacheKey); // Get cached with the given cache key
if (cached && Date.now() - cached.timestamp < 5 * 60 * 1000) {
return cached.data; // Return cached data with the given cached data
}
const products = await fetch("/api/products").then(r => r.json()); // Fetch products with the given api route and get data from response
cache.set(cacheKey, { data: products, timestamp: Date.now() }); // Set cache with the given cache key and data with the given data and timestamp with the given timestamp
return products; // Return products with the given products
}9. Form Validation
Validate forms both client-side and server-side:
const validateForm = (data) => { // Validate form with the given data
const errors = {};
if (!data.email) {
errors.email = "Email is required"; // Set email required error with the given email required error
} else if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(data.email)) {
errors.email = "Invalid email format"; // Set invalid email format error with the given invalid email format error
}
if (!data.password) {
errors.password = "Password is required"; // Set password required error with the given password required error
} else if (data.password.length < 8) {
errors.password = "Password must be at least 8 characters"; // Set password must be at least 8 characters error with the given password must be at least 8 characters error
}
return errors;
};10. Performance Optimization
- Use Server Components: Default to Server Components, only use Client Components when needed
- Minimize JavaScript: Keep client-side JavaScript minimal
- Lazy Load Images: Load images as they come into view
- Debounce Search: Debounce search inputs to reduce API calls
- Batch Requests: Combine multiple API requests when possible
11. Security Best Practices
- Sanitize Input: Always sanitize user input
- Validate Server-Side: Never trust client-side validation alone
- Use HTTPS: Always use HTTPS in production
- Secure Cookies: Use httpOnly, secure, and sameSite flags
- Rate Limiting: Implement rate limiting for API endpoints
- CSRF Protection: Protect against CSRF attacks
12. Code Organization
- Component Structure: Keep components in logical folders
- Reusable Logic: Extract reusable logic to custom hooks
- Constants: Store constants in separate files
- Type Definitions: Use TypeScript or PropTypes for type safety
- Documentation: Add comments for complex logic
13. Testing
Write tests for critical functionality:
- Unit tests for utility functions
- Integration tests for API routes
- Component tests for UI components
- E2E tests for critical user flows
Remember: Following best practices ensures your application is maintainable, secure, and performant.