# Deployment and DevOps Standards for Vercel
This document outlines the deployment and DevOps standards for projects hosted on Vercel. Adhering to these standards ensures maintainability, performance, security, and a smooth development lifecycle. These standards are aligned with the most recent Vercel features and best practices.
## 1. Build Processes and CI/CD
### 1.1. Standard: Automated Builds and Deployments
**Description:** Every code change should trigger an automated build, test, and deployment pipeline. This ensures consistent deployments and reduces human error.
**Why:** Automated pipelines improve deployment speed, repeatability, and reliability. They also enable faster feedback loops and easier rollbacks.
**Do This:**
* Use Vercel's built-in Git integration for automatic deployments on every push to a branch configure in your Vercel project settings.
* Utilize Vercel's Preview Deployments for every pull request.
* Configure environment variables appropriately for different Vercel environments (Production, Preview, Development).
**Don't Do This:**
* Manually deploy code changes through the Vercel CLI without proper Git integration.
* Store sensitive information (API keys, database passwords) directly in code or the repository.
* Bypass the automated build process for what you consider "small changes".
**Code Example (vercel.json configuration):**
"""json
// vercel.json
{
"version": 2,
"builds": [
{
"src": "package.json",
"use": "@vercel/next"
}
],
"routes": [
{
"src": "/api/(.*)",
"dest": "/api/$1"
},
{
"src": "/(.*)",
"dest": "/$1"
}
],
"env": {
"DATABASE_URL": "@database-url",
"NODE_ENV": "production"
},
"probes": [
{
"path": "/api/health",
"readiness": true,
"initialDelay": 3000,
"interval": 5000
}
]
}
"""
**Explanation:**
* "version": Specifies the Vercel configuration version.
* "builds": Defines how to build the project. In this case it uses "@vercel/next" to deploy next.js.
* "routes": Defines how incoming requests are routed to different parts of your application.
* "env": Defines environment variables which are crucial for differing configurations across environments. Using "@database-url" allows referencing secrets stored in Vercel rather than directly storing values.
* "probes": Defines health check endpoints for the Vercel platform to determine the operational status of your application.
**Anti-Pattern:** Committing API keys directly in the repository.
"""javascript
// DON'T DO THIS:
const API_KEY = "YOUR_SECRET_API_KEY";
"""
Instead, utilize Vercel's environment variables or secrets store:
"""javascript
// DO THIS:
const API_KEY = process.env.API_KEY; // Configure API_KEY in Vercel's settings
"""
### 1.2. Standard: Preview Environments
**Description:** Each pull request should automatically create a unique preview environment on Vercel.
**Why:** Allows stakeholders to review and test changes in a production-like environment before merging them into the main branch. This reduces the risk of introducing bugs into production.
**Do This:**
* Utilize Vercel's automatic preview deployment feature on all pull requests.
* Include meaningful names in the pull requests. Vercel will use these names in naming preview environments automatically, increasing traceability.
* Test environment-specific features using environment variables.
**Don't Do This:**
* Disable Vercel's preview deployment feature.
* Assume that code works on your local machine without deploying to a preview environment first.
* Merge untested or unreviewed code into the main branch.
**Code Example (Environment Variable for Preview Environments):**
"""javascript
// utils/config.js
const isProduction = process.env.NODE_ENV === 'production';
const isPreview = process.env.VERCEL_ENV === 'preview';
const config = {
apiUrl: isProduction ? 'https://your-production-api.com' : (isPreview ? 'https://your-preview-api.com' : 'http://localhost:3001'),
featureFlagEnabled: isProduction || isPreview // Enable production/preview features only
};
export default config;
"""
**Explanation:**
* Checks the "NODE_ENV" for production environments.
* Checks the "VERCEL_ENV" to identify preview environment. "VERCEL_ENV" is automatically set by Vercel.
* Dynamically sets "apiUrl" based on the environment, allowing the app to connect to the correct backend in each context.
**Anti-Pattern:** Hardcoding environment-specific configurations directly into components:
"""javascript
// DON'T DO THIS:
function MyComponent() {
const apiUrl = window.location.hostname === 'your-production-url.com' ? 'https://your-production-api.com' : 'http://localhost:3001';
// ...
}
"""
### 1.3. Standard: Staging Environments
**Description:** Utilize Vercel's branch aliasing feature to create a staging environment. This environment mirrors production closely for final testing.
**Why:** Provides a dedicated environment for rigorous testing and stakeholder approval before promoting changes to production.
**Do This:**
* Alias a specific branch such as 'staging' (or a mirror of 'main' or 'production') the "staging" environment in Vercel.
* Run end-to-end tests and user acceptance tests in the staging environment.
* Use the staging environment to validate database migrations and other infrastructure changes.
**Don't Do This:**
* Treat the staging environment as a playground for development.
* Skip staging and deploy directly production after development.
* Allow untested code from feature branches to exist in staging.
**Code Example (vercel.json to specifically manage staging):**
"""json
{
"version": 2,
"alias": [
{
"source": "staging.your-project.vercel.app",
"destination": "your-project.vercel.app/staging"
}
]
}
"""
**Explanation:**
This alias configuration maps the "staging" subdomain to a specific internal route on Vercel. When deploying to the "staging" environment (configured in Vercel project settings), Vercel will ensure that builds utilize this configuration. You'd push code to the "staging" branch (or whatever your staging branch name is) in Git and Vercel will handle the deployment to "staging.your-project.vercel.app".
**Anti-Pattern:** Using the production database or live user data within the staging environment. Always use a separate database or anonymized data for staging.
## 2. Production Considerations
### 2.1. Standard: Monitoring and Error Tracking
**Description:** Implement comprehensive monitoring and error tracking to identify and resolve issues in production.
**Why:** Real-time visibility into application health is crucial for maintaining uptime and performance. Proactive monitoring enables quick responses to unexpected errors.
**Do This:**
* Use Vercel's built-in analytics or integrate with third-party monitoring tools. Sentry is a popular choice.
* Implement proper logging at different levels (info, warning, error) using structured logging.
* Set up alerts for critical errors and performance bottlenecks.
**Don't Do This:**
* Rely solely on user reports to identify issues.
* Ignore error logs or alerts.
* Log sensitive data.
**Code Example (Frontend Error Tracking with Sentry and Next.js):**
"""javascript
// pages/_app.js
import * as Sentry from "@sentry/nextjs";
Sentry.init({
dsn: process.env.NEXT_PUBLIC_SENTRY_DSN,
integrations: [new Sentry.BrowserTracing(), new Sentry.Replay()],
tracesSampleRate: 0.1,
replaysSessionSampleRate: 0.1,
replaysOnErrorSampleRate: 1.0,
environment: process.env.NODE_ENV
});
function MyApp({ Component, pageProps }) {
return ;
}
export default MyApp;
"""
"""javascript
// pages/api/error.js
import * as Sentry from "@sentry/nextjs";
function handler(req, res) {
try {
throw new Error("Testing Sentry Error Tracking!");
} catch (error) {
Sentry.captureException(error);
res.status(500).json({ message: "Error reported to Sentry!" });
}
}
export default Sentry.withSentry(handler);
"""
**Explanation:**
* The example initializes Sentry in "_app.js" to capture client-side errors. Browser tracing is enabled for performance monitoring. Replays capture user sessions when errors occur, which aids in debugging front-end issues.
* The API route example demonstrates how to manually capture exceptions within the backend code. "Sentry.captureException(error)" logs the error in Sentry. Further, Sentry's API route enhancement is used with "Sentry.withSentry(handler)".
**Anti-Pattern:** Logging excessively without structured formatting, making it challenging to analyze logs.
### 2.2. Standard: Rollback Strategy
**Description:** Have a well-defined rollback strategy in place in case of a failed deployment.
**Why:** Minimizes downtime and reduces impact on users when a deployment introduces critical errors.
**Do This:**
* Utilize Vercel's automatic rollback feature (if available and if the platform you use supports rollback) to revert to the previous deployment.
* Maintain a backup of your database and application configuration.
* Test the rollback strategy in the staging environment.
**Don't Do This:**
* Attempt to fix issues directly in production without a rollback plan.
* Make significant database changes without a proper backup.
**Code Example (Manual Rollback via Vercel CLI - illustrative, automatic rollbacks are preferred):**
"""bash
# Assuming you want to rollback to a specific deployment ID:
vercel alias production .vercel.app your-project.vercel.app
"""
**Explanation:**
This command (for illustration only, check Vercel's latest documentation for correct CLI usage) would alias the specified deployment (identified by "") back to the production domain.
**Anti-Pattern:** Not having any rollback plan, relying on fixing problems in production without a safety net.
### 2.3. Standard: Performance Optimization
**Description:** Continuously monitor and optimize application performance to ensure a fast and responsive user experience.
**Why:** Performance is crucial for user satisfaction, SEO ranking, and conversion rates. Optimized applications reduce infrastructure costs.
**Do This:**
* Use Vercel's built-in performance analytics to identify bottlenecks.
* Optimize images using tools like "next/image" or Vercel's Image Optimization API.
* Implement code splitting to reduce initial load time.
* Cache frequently accessed data using Vercel's Edge Cache or a dedicated caching layer.
* Monitor and optimize database queries.
* Use CDN features to distribute static assets geographically for faster delivery.
**Don't Do This:**
* Ignore performance metrics.
* Load large, unoptimized images.
* Serve unnecessary JavaScript or CSS.
**Code Example (Image Optimization with "next/image"):**
"""jsx
import Image from 'next/image'
function MyComponent() {
return (
)
}
export default MyComponent;
"""
**Explanation:**
* "next/image" component automatically optimizes images for different devices and viewports. It lazy loads images to improve initial page load.
**Anti-Pattern:** Serving large, uncompressed images directly:
"""html
// DON'T DO THIS:
"""
## 3. Security Best Practices
### 3.1. Standard: Secure Environment Variables
**Description:** Store all sensitive information (API keys, database passwords, etc.) as environment variables.
**Why:** Prevents accidental exposure of secrets in code or version control.
**Do This:**
* Use Vercel's environment variables feature to store secrets.
* Avoid hardcoding secrets directly in the codebase.
* Rotate keys regularly.
**Don't Do This:**
* Commit secrets to version control.
* Expose secrets in client-side code.
**Code Example (Accessing Environment Variables):**
"""javascript
const API_KEY = process.env.API_KEY;
// Use the API_KEY in your application logic
"""
**Explanation:**
* "process.env.API_KEY" retrieves the value of the "API_KEY" environment variable. Configure this variable in the Vercel project settings.
**Anti-Pattern:** Hardcoding API keys directly into the code.
### 3.2. Standard: Dependency Management
**Description:** Keep dependencies up-to-date to patch security vulnerabilities.
**Why:** Outdated dependencies often contain known vulnerabilities that can be exploited by attackers.
**Do This:**
* Regularly update dependencies using "npm update", "yarn upgrade", or similar tools.
* Use dependency scanning tools like "npm audit" or "yarn audit" to identify vulnerabilities.
* Automate dependency updates using tools like Dependabot.
**Don't Do This:**
* Ignore security warnings from dependency scanning tools.
* Use outdated dependencies without assessing and mitigating the risks.
**Code Example (Using "npm audit"):**
"""bash
npm audit
"""
**Explanation:**
* "npm audit" scans the project's dependencies for known vulnerabilities and provides recommendations on how to fix them.
### 3.3. Standard: Input Validation and Sanitization
**Description:** Validate and sanitize all user inputs to prevent injection attacks.
**Why:** Prevents malicious users from injecting code or manipulating data through application inputs.
**Do This:**
* Use appropriate validation libraries to validate user input.
* Escape or sanitize user input before displaying it in the UI or storing it in the database.
* Implement rate limiting to prevent brute-force attacks.
**Don't Do This:**
* Trust user input without validation.
* Expose raw user input in SQL queries or HTML.
**Code Example (Input Validation in a Next.js API Route):**
"""javascript
import { check, validationResult } from 'express-validator';
export default async function handler(req, res) {
await check('email').isEmail().run(req);
await check('password').isLength({ min: 5 }).run(req);
const result = validationResult(req);
if (!result.isEmpty()) {
return res.status(400).json({ errors: result.array() });
}
// Process the validated input
const { email, password } = req.body;
// ...
}
"""
**Explanation:**
* "express-validator" is used to validate the "email" and "password" fields in the request body.
* If validation fails, an error response is returned with the validation errors.
## 4. Modern Deployment Patterns
### 4.1. Standard: GitOps
**Description:** Adopt a GitOps approach where deployments are triggered by changes in the Git repository.
**Why:** Improves auditability, consistency, and repeatability of deployments.
**Do This:**
* Treat your Vercel configuration (e.g., "vercel.json") as code and store it in the Git repository.
* Use pull requests to propose and review changes to the deployment configuration.
* Automate deployments based on Git events (e.g., pushes, merges).
**Don't Do This:**
* Make manual changes to the Vercel configuration outside of the Git repository.
* Bypass the Git-based deployment process.
**Code Example (Illustrative - GitOps Workflow):**
1. Developer creates a new feature branch and makes changes.
2. Developer submits a pull request with changes to the "vercel.json" file or application code.
3. Vercel automatically creates a preview deployment for the pull request.
4. Reviewers approve the pull request.
5. Merging the pull request to the main/production branch triggers a production deployment.
### 4.2. Standard: Infrastructure as Code (IaC)
**Description:** Manage your Vercel infrastructure and configuration using code.
**Why:** Improves consistency, repeatability, and version control of infrastructure.
**Do This:**
* Not directly applicable to Vercel in the traditional cloud provider sense (AWS CloudFormation, Terraform), but the "vercel.json" file represents the "infrastructure" definition for your deployments on Vercel. Treat this with IaC principles.
* Store the Vercel configuration (e.g., "vercel.json") in the Git repository.
* Automate infrastructure provisioning and updates using CI/CD pipelines.
* Define Vercel environment variables and secrets as code
**Don't Do This:**
* Manually configure Vercel projects through the UI.
* Fail to version control.
### 4.3. Standard: Edge Computing and Vercel Edge Functions
**Description:** Leverage Vercel Edge Functions to execute code at the edge of the network, closer to users.
**Why:** Reduces latency, improves performance, and enhances user experience.
**Do This:**
* Use Edge Functions for tasks such as authentication, authorization, A/B testing, and personalization.
* Optimize Edge Functions for performance and security.
* Monitor Edge Function execution time and error rates.
**Don't Do This:**
* Use Edge Functions for long-running or computationally intensive tasks. offload those to serverless functions.
* Store sensitive data directly in Edge Functions.
**Code Example (Basic Vercel Edge Function with Next.js):**
"""javascript
// pages/api/edge.js
export const config = {
runtime: 'edge',
};
export default async function handler(req) {
const data = {
message: 'Hello from the Edge!',
time: new Date().toISOString(),
};
return new Response(JSON.stringify(data), {
status: 200,
headers: {
'content-type': 'application/json',
},
});
}
"""
**Explanation:**
* "runtime: 'edge'" configures the function to run at the edge using Vercel's Edge Runtime.
* The function returns a JSON response with a message and the current time.
## 5. Vercel-Specific Considerations
### 5.1. Standard: Utilize Vercel Remote Cache
**Description:** Leverage the Vercel Remote Cache with Turborepo to drastically cut down on build times.
**Why:** Build caching significantly accelerates the deployment process by reusing already-built artifacts.
**Do This:**
* Enable the Vercel Remote Cache inside of your Vercel project settings when using Turborepo.
* Ensure Turborepo is configured properly in your "turbo.json" to take advantage of caching.
**Don't Do This:**
* Disable remote caching when viable, as it adds significant time savings.
* Store senstive info or rely on the Vercel Remote Cache as your ONLY source of truth. It's designed as a temporary performance enhancer, not persistant storage.
"""json
// turbo.json
{
"pipeline": {
"build": {
"dependsOn": ["^build"],
"outputs": [".next/**", "!.next/cache/**"]
},
"test": {},
"lint": {}
},
"globalDependencies": ["tsconfig.json"],
"globalEnv": ["NODE_ENV", "VERCEL"]
}
"""
**Explanation:** In "turbo.json" we specify our turborepo configuration. "outputs" dictate which files are cached after a build is complete.
### 5.2. Standard: Prioritize Vercel Functions Configuration Options
**Description:** Properly configure Vercel Functions to manage runtime effectively based on workload profile.
**Why:** Improper configuration can lead to unexpected costs or performance constraints.
**Do This:**
* Understand and configure "memory", "maxDuration", Regions, and "concurrency" based on the funciton workload.
**Don't Do This:**
* Use default settings for all functions. Review each function's needs.
* Over-allocate resources without justification.
"""json
// vercel.json
{
"functions": {
"api/heavy-computation.js": {
"memory": 2048,
"maxDuration": 60,
"regions": ["iad1"]
},
"api/image-resize.js": {
"memory": 512,
"maxDuration": 10,
"regions": ["sfo1", "iad1", "fra1"]
}
}
}
"""
**Explanation:**
* The configuration shows how to configure maximum memory, maximum duration, and regions on a per-function basis.
* Tailor these settings to each functions needs.
By consistently adhering to these Deployment and DevOps standards, development teams can build, deploy, and maintain high-quality applications on Vercel with improved efficiency, reliability, and security.
danielsogl
Created Mar 6, 2025
This guide explains how to effectively use .clinerules
with Cline, the AI-powered coding assistant.
The .clinerules
file is a powerful configuration file that helps Cline understand your project's requirements, coding standards, and constraints. When placed in your project's root directory, it automatically guides Cline's behavior and ensures consistency across your codebase.
Place the .clinerules
file in your project's root directory. Cline automatically detects and follows these rules for all files within the project.
# Project Overview project: name: 'Your Project Name' description: 'Brief project description' stack: - technology: 'Framework/Language' version: 'X.Y.Z' - technology: 'Database' version: 'X.Y.Z'
# Code Standards standards: style: - 'Use consistent indentation (2 spaces)' - 'Follow language-specific naming conventions' documentation: - 'Include JSDoc comments for all functions' - 'Maintain up-to-date README files' testing: - 'Write unit tests for all new features' - 'Maintain minimum 80% code coverage'
# Security Guidelines security: authentication: - 'Implement proper token validation' - 'Use environment variables for secrets' dataProtection: - 'Sanitize all user inputs' - 'Implement proper error handling'
Be Specific
Maintain Organization
Regular Updates
# Common Patterns Example patterns: components: - pattern: 'Use functional components by default' - pattern: 'Implement error boundaries for component trees' stateManagement: - pattern: 'Use React Query for server state' - pattern: 'Implement proper loading states'
Commit the Rules
.clinerules
in version controlTeam Collaboration
Rules Not Being Applied
Conflicting Rules
Performance Considerations
# Basic .clinerules Example project: name: 'Web Application' type: 'Next.js Frontend' standards: - 'Use TypeScript for all new code' - 'Follow React best practices' - 'Implement proper error handling' testing: unit: - 'Jest for unit tests' - 'React Testing Library for components' e2e: - 'Cypress for end-to-end testing' documentation: required: - 'README.md in each major directory' - 'JSDoc comments for public APIs' - 'Changelog updates for all changes'
# Advanced .clinerules Example project: name: 'Enterprise Application' compliance: - 'GDPR requirements' - 'WCAG 2.1 AA accessibility' architecture: patterns: - 'Clean Architecture principles' - 'Domain-Driven Design concepts' security: requirements: - 'OAuth 2.0 authentication' - 'Rate limiting on all APIs' - 'Input validation with Zod'
# Code Style and Conventions Standards for Vercel This document outlines the coding style and conventions standards for Vercel projects. Adhering to these standards contributes to code that is readable, maintainable, and performant within the Vercel ecosystem. These guidelines are tailored for the latest Vercel features and are designed to enhance collaboration, reduce bugs, and improve overall project quality. ## 1. General Principles * **Consistency:** Follow a consistent style throughout the entire project. Use a style guide and linter to enforce consistency automatically. * **Readability:** Write code that is easy to understand. Use meaningful names, clear comments, and proper indentation. * **Maintainability:** Design code that is easy to modify and extend. Separate concerns, avoid overly complex logic, and write modular code. * **Performance:** Strive for optimal performance. Consider the impact of code choices on rendering times, API response times, and overall user experience. * **Security:** Protect against common web vulnerabilities. Sanitize inputs, validate data, and follow security best practices for each technology stack. ## 2. Formatting ### 2.1. Code Formatting Tools * **Do This:** Use Prettier to automatically format code. Vercel recommends integrating Prettier with ESLint for comprehensive code quality checks. * **Don't Do This:** Rely solely on manual formatting. Manual formatting is error-prone and time-consuming. **Why:** Prettier ensures consistent formatting across the entire codebase, regardless of individual developer preferences. This improves readability and reduces merge conflicts caused by formatting differences. **Example "package.json" setup:** """json { "devDependencies": { "prettier": "^3.0.0", // Use the latest version "eslint-config-prettier": "^9.0.0", // Use the latest version "eslint-plugin-prettier": "^5.0.0" // Use the latest version }, "scripts": { "format": "prettier --write ." } } """ **Example ".prettierrc.js" (or ".prettierrc.json") configuration:** """javascript module.exports = { semi: true, trailingComma: "all", singleQuote: true, printWidth: 120, tabWidth: 2, }; """ ### 2.2. Indentation * **Do This:** Use 2 spaces for indentation. This is the standard for JavaScript and React. * **Don't Do This:** Use tabs or 4 spaces. These are less common and can lead to inconsistencies. **Why:** Consistent indentation improves code readability. 2 spaces are generally preferred for JavaScript as they provide a good balance between readability and code density. **Example:** """javascript function myFunction() { if (condition) { console.log('This is properly indented.'); } } """ ### 2.3. Line Length * **Do This:** Aim for a maximum line length of 120 characters. * **Don't Do This:** Exceed 120 characters without a clear reason. **Why:** Shorter lines improve readability, especially on smaller screens. A reasonable line length makes it easier to scan code and understand its structure. ### 2.4. Whitespace * **Do This:** Use whitespace to separate logical blocks of code. Add blank lines between functions, classes, and other significant sections. * **Don't Do This:** Omit necessary whitespace or add excessive whitespace. **Why:** Proper whitespace enhances readability by visually separating different parts of the code. **Example:** """javascript function calculateSum(a, b) { return a + b; } function calculateProduct(a, b) { return a * b; } """ ## 3. Naming Conventions ### 3.1. Variables * **Do This:** Use camelCase for variable names. * **Don't Do This:** Use snake_case or PascalCase for variable names (except for classes/components). **Why:** camelCase is the standard naming convention for variables in JavaScript. **Example:** """javascript const firstName = 'John'; let userAge = 30; """ ### 3.2. Functions * **Do This:** Use camelCase for function names. Use descriptive names that clearly indicate the function's purpose. * **Don't Do This:** Use vague or abbreviated function names. **Why:** Clear function names improve code readability and maintainability. **Example:** """javascript function getUserData() { // ... } function calculateTotalPrice() { // ... } """ ### 3.3. Classes/Components * **Do This:** Use PascalCase for class and component names. * **Don't Do This:** Use camelCase or snake_case for class/component names. **Why:** PascalCase is the standard naming convention for classes and components in JavaScript/React. **Example (React):** """javascript function UserProfile() { return ( <div> {/* ... */} </div> ); } export default UserProfile; class ApiClient { // ... } """ ### 3.4. Constants * **Do This:** Use SCREAMING_SNAKE_CASE for constant names. * **Don't Do This:** Use camelCase or PascalCase for constant names. **Why:** SCREAMING_SNAKE_CASE clearly identifies a variable as a constant. **Example:** """javascript const API_URL = 'https://api.example.com'; const MAX_USERS = 100; """ ### 3.5. File Names * **Do This:** Use kebab-case for file names. Use descriptive names that indicate the file's contents. * **Don't Do This:** Use camelCase or snake_case for file names. Avoid generic file names like "utils.js". **Why:** kebab-case is a common convention for file names, especially in web development. Descriptive file names improve project organization and make it easier to find specific files. **Example:** """ components/user-profile.js utils/date-formatter.js api/get-user-data.js """ ## 4. Stylistic Consistency ### 4.1. Quotes * **Do This:** Use single quotes ("'") for string literals in JavaScript. * **Don't Do This:** Use double quotes (""") unless there's a specific reason (e.g., containing a single quote). **Why:** Single quotes are generally preferred in JavaScript for consistency and because they are more easily typed. Prettier can enforce this automatically. **Example:** """javascript const message = 'Hello, world!'; """ ### 4.2. Semicolons * **Do This:** Always include semicolons at the end of statements. Although JavaScript has automatic semicolon insertion (ASI), it's safer to include them explicitly to avoid unexpected behavior. * **Don't Do This:** Rely on ASI. **Why:** Explicit semicolons prevent potential parsing errors and improve code clarity. **Example:** """javascript const x = 10; console.log(x); """ ### 4.3. Comments * **Do This:** Write clear and concise comments to explain complex logic, non-obvious code, and intentions. Use JSDoc for documenting functions and classes. * **Don't Do This:** Write unnecessary comments that simply restate the code. Avoid excessive or outdated comments. **Why:** Comments are crucial for code maintainability and collaboration. They help other developers (and your future self) understand the logic and purpose of the code. **Example:** """javascript /** * Calculates the area of a rectangle. * * @param {number} width - The width of the rectangle. * @param {number} height - The height of the rectangle. * @returns {number} The area of the rectangle. */ function calculateRectangleArea(width, height) { // Multiply width and height to get the area. return width * height; } """ ### 4.4. Error Handling * **Do This:** Implement robust error handling using "try...catch" blocks and error logging. Leverage Vercel's logging capabilities for server-side error tracking. * **Don't Do This:** Ignore potential errors or rely solely on unhandled exceptions. **Why:** Proper error handling prevents application crashes, provides informative error messages, and helps identify and resolve issues quickly. **Example (Serverless Function with error handling):** """javascript module.exports = async (req, res) => { try { const data = await fetchDataFromExternalAPI(); res.status(200).json(data); } catch (error) { console.error('Error fetching data:', error); // Log to Vercel logs res.status(500).json({ error: 'Failed to fetch data' }); } }; """ ### 4.5. Code Organization * **Do This:** Organize code into logical modules and components. Separate concerns and avoid tightly coupled code. * **Don't Do This:** Write monolithic files or functions that are difficult to understand and maintain. **Why:** Modular code is easier to understand, test, and reuse. It also facilitates collaboration and reduces the risk of introducing bugs during modifications. **Example (React Component Structure):** """ /components ├── UserProfile │ ├── UserProfile.jsx │ ├── UserProfile.module.css │ └── index.js └── Button ├── Button.jsx ├── Button.module.css └── index.js """ "UserProfile/index.js": """javascript export { default } from './UserProfile'; """ ## 5. Vercel-Specific Considerations ### 5.1. Serverless Functions * **Do This:** Keep serverless functions small and focused. Optimize for cold starts. Use environment variables for configuration. * **Don't Do This:** Write overly complex serverless functions that perform multiple tasks. Hardcode sensitive information in the function code. **Why:** Vercel serverless functions are designed for short-lived tasks. Minimizing their size and complexity improves performance and reduces costs. Environment variables provide a secure and flexible way to configure functions. **Example (Environment Variables in Vercel UI):** Define API keys and other secrets in the Vercel project settings under "Environment Variables". Then, access them in your serverless function: """javascript module.exports = async (req, res) => { const apiKey = process.env.API_KEY; if (!apiKey) { console.error('API_KEY is not defined in environment variables.'); return res.status(500).json({ error: 'API Key not configured' }); } // ... use apiKey to make API calls ... }; """ ### 5.2. Edge Functions * **Do This:** Use Edge Functions for tasks that require low latency and global distribution. Optimize for performance by minimizing code size and dependencies. Test thoroughly in the Vercel environment. * **Don't Do This:** Use Edge Functions for computationally intensive tasks. **Why:** Edge Functions execute on Vercel's global edge network, providing extremely low latency. However, they have limited resources and are best suited for tasks like authentication, authorization, and A/B testing. **Example (Basic Edge Function for Redirects):** """javascript export const config = { matcher: '/old-path', }; export function middleware(request) { const url = new URL('/new-path', request.url); return Response.redirect(url); } """ ### 5.3. Vercel KV (Redis) * **Do This:** Utilize Vercel KV for caching and session management. Use appropriate expiration times for cached data. * **Don't Do This:** Store sensitive information directly in Vercel KV without proper encryption. **Why:** Vercel KV provides a fast and reliable key-value store that is well-integrated with the Vercel platform. **Example (Using Vercel KV):** """javascript import { kv } from '@vercel/kv'; module.exports = async (req, res) => { const key = 'my-data'; const cachedData = await kv.get(key); if (cachedData) { return res.status(200).json({ data: cachedData, source: 'cache' }); } const data = await fetchDataFromExternalAPI(); await kv.set(key, data, { ex: 3600 }); // Cache for 1 hour res.status(200).json({ data, source: 'api' }); }; """ ### 5.4. Vercel Blob * **Do This:** Use Vercel Blob for storing and serving large files. Optimize images before uploading. * **Don't Do This:** Store sensitive files without proper access control. **Why:** Vercel Blob provides a scalable and cost-effective solution for storing and serving static assets. **Example (Uploading a file to Vercel Blob):** """javascript import { put } from '@vercel/blob'; export const config = { api: { bodyParser: false, }, }; export default async function handler(req, res) { if (req.method === 'POST') { try { const file = req.files.file; // Assuming you are using a library like formidable const { url } = await put(file.name, file.data, { access: 'public', }); res.status(200).json({ url }); } catch (error) { console.error('Error uploading file:', error); res.status(500).json({ error: 'Failed to upload file' }); } } else { res.status(405).json({ error: 'Method Not Allowed' }); } } """ ### 5.5. Vercel Analytics and Logging * **Do This:** Utilize Vercel Analytics to monitor performance and identify issues. Implement structured logging to capture relevant data points. * **Don't Do This:** Ignore performance metrics or rely solely on manual debugging. **Why:** Vercel Analytics provides valuable insights into application performance. Structured logging allows for efficient analysis and troubleshooting. Vercel provides integrated logging automatically. Using "console.log", "console.warn", and "console.error" will automatically send logs to the Vercel platform. ## 6. Performance Optimization ### 6.1. Code Splitting * **Do This:** Use code splitting to reduce the initial bundle size and improve page load times. * **Don't Do This:** Load all code upfront. **Why:** Code splitting allows you to load only the necessary code for each page or component, reducing the initial load time and improving the user experience. Modern frameworks like Next.js support code splitting out of the box. **Example (Next.js Dynamic Import):** """javascript import dynamic from 'next/dynamic'; const MyComponent = dynamic(() => import('../components/MyComponent'), { loading: () => <p>Loading...</p>, }); function MyPage() { return ( <div> <h1>My Page</h1> <MyComponent /> </div> ); } export default MyPage; """ ### 6.2. Image Optimization * **Do This:** Optimize images by compressing them and using appropriate formats (e.g., WebP). Use responsive images to serve different sizes based on the device. Utilize Next.js's built-in image optimization. * **Don't Do This:** Serve large, unoptimized images. **Why:** Image optimization significantly improves page load times and reduces bandwidth consumption. **Example (Next.js Image Component):** """javascript import Image from 'next/image'; import myImage from '../public/my-image.jpg'; function MyComponent() { return ( <Image src={myImage} alt="My Image" width={500} height={300} /> ); } export default MyComponent; """ ### 6.3. Caching * **Do This:** Implement caching strategies to reduce server load and improve response times. Use browser caching, CDN caching, and server-side caching where appropriate. Use Vercel KV for server-side caching. * **Don't Do This:** Over-cache data or fail to invalidate the cache when data changes. **Why:** Caching can dramatically improve performance by serving data from a cache instead of repeatedly fetching it from the server. ### 6.4. Minimize Dependencies * **Do This:** Carefully evaluate dependencies and remove any unnecessary ones. Use tree shaking to eliminate unused code from dependencies. Consider using lighter alternatives. * **Don't Do This:** Add dependencies without considering their impact on bundle size and performance. **Why:** Reducing the number of dependencies and minimizing their size improves bundle size, reduces load times, and decreases the risk of security vulnerabilities. ## 7. Security Best Practices ### 7.1. Input Validation * **Do This:** Validate all user inputs to prevent injection attacks and other security vulnerabilities. * **Don't Do This:** Trust user inputs without validation. **Why:** Input validation is crucial for preventing security exploits. **Example (Simple Input Validation):** """javascript module.exports = async (req, res) => { const { email } = req.body; if (!email) { return res.status(400).json({ error: 'Email is required' }); } if (!isValidEmail(email)) { return res.status(400).json({ error: 'Invalid email format' }); } // ... process valid email ... }; function isValidEmail(email) { // Basic email validation regex const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; return emailRegex.test(email); } """ ### 7.2. Output Encoding * **Do This:** Encode all outputs to prevent cross-site scripting (XSS) attacks. * **Don't Do This:** Output user-provided data directly without encoding. **Why:** Output encoding prevents malicious scripts from being injected into the page. ### 7.3. Authentication and Authorization * **Do This:** Implement secure authentication and authorization mechanisms. Use strong passwords and multi-factor authentication. Always use HTTPS. Consider using Vercel's integration with authentication providers. * **Don't Do This:** Store passwords in plain text or use weak authentication methods. **Why:** Proper authentication and authorization protect sensitive data and prevent unauthorized access. ### 7.4. Dependency Management * **Do This:** Keep dependencies up to date to patch security vulnerabilities. Use a dependency management tool like npm or yarn to manage dependencies. * **Don't Do This:** Use outdated dependencies with known vulnerabilities. **Why:** Regularly updating dependencies ensures that you have the latest security patches. This comprehensive guide provides a robust foundation for developing high-quality, maintainable, and secure Vercel applications. By adhering to these coding style and convention standards, development teams can significantly improve their efficiency and the overall quality of their projects. Remember to adapt these guidelines to the specific requirements of your project and to stay updated with the latest Vercel features and best practices.
# Testing Methodologies Standards for Vercel This document outlines the testing methodologies standards for Vercel projects, aiming to ensure high-quality, reliable, and performant applications. It covers unit, integration, and end-to-end testing strategies, with a focus on Vercel-specific considerations and modern best practices. ## 1. General Testing Principles ### 1.1. Test-Driven Development (TDD) **Do This:** Embrace Test-Driven Development (TDD) where possible. Write tests before implementing the actual code. This helps clarify requirements and design, leading to more focused and testable code. **Don't Do This:** Write tests as an afterthought. This often results in inadequate test coverage and makes refactoring more difficult. **Why:** TDD promotes better code design, reduces bugs, and improves maintainability. **Example:** """typescript // Example: TDD Approach for a function that greets a user // 1. Write the test first (using Jest, for example) describe('greetUser', () => { it('should return a greeting message with the user\'s name', () => { const greeting = greetUser('Alice'); expect(greeting).toBe('Hello, Alice!'); }); it('should handle empty names gracefully', () => { const greeting = greetUser(''); expect(greeting).toBe('Hello, !'); }); }); // 2. Then, implement the function to make the tests pass function greetUser(name: string): string { return "Hello, ${name}!"; } """ ### 1.2. Testing Pyramid **Do This:** Follow the testing pyramid principles: a large base of unit tests, a medium layer of integration tests, and a small cap of end-to-end tests. **Don't Do This:** Rely solely on end-to-end tests. They are slower, more brittle, and provide less specific feedback. **Why:** A balanced testing approach provides comprehensive coverage while optimizing for speed and maintainability. ### 1.3. Code Coverage **Do This:** Aim for a high code coverage percentage (e.g., 80% or higher) but prioritize testing critical logic and edge cases over simply achieving a number. Use tools like "nyc", "c8", or "jest" with coverage reports for measurement. **Don't Do This:** Aim for 100% code coverage at all costs. It can lead to over-testing simple code and neglecting complex scenarios. **Why:** Code coverage provides a metric to track the proportion of code exercised by tests, but it should be used as a guide, not the goal. ### 1.4. Automated Testing **Do This:** Integrate automated testing into your CI/CD pipeline using Vercel's built-in features, GitHub Actions, or other CI tools. Run tests on every commit and pull request. **Don't Do This:** Rely on manual testing alone. It's time-consuming, error-prone, and doesn't scale. **Why:** Automation ensures consistent and reliable testing, catching regressions early in the development process. Vercel's preview deployments are excellent for integrating automated tests. ### 1.5. Mocking and Stubbing **Do This:** Use mocking and stubbing to isolate units of code during testing. Tools like "jest.mock()" or the "sinon" library can be valuable here. **Don't Do This:** Mock excessively. Over-mocking can lead to tests that don't accurately reflect real-world behavior. **Why:** Mocking allows you to control the behavior of dependencies, making tests more predictable and faster to execute. ## 2. Unit Testing ### 2.1. Scope **Do This:** Unit test individual functions, classes, or components in isolation. Focus on verifying the logic and behavior within that specific unit. **Don't Do This:** Write unit tests that depend on external services or databases. These become integration tests. **Why:** Unit tests are fast, focused, and provide detailed feedback about specific code units. **Example:** """typescript // Example: Unit testing a utility function // utility.ts export function capitalize(str: string): string { if (!str) return ''; return str.charAt(0).toUpperCase() + str.slice(1); } // utility.test.ts (using Jest) import { capitalize } from './utility'; describe('capitalize', () => { it('should capitalize the first letter of a string', () => { expect(capitalize('hello')).toBe('Hello'); }); it('should handle an empty string', () => { expect(capitalize('')).toBe(''); }); it('should handle a string with only one character', () => { expect(capitalize('a')).toBe('A'); }); }); """ ### 2.2. Test Cases **Do This:** Cover all possible scenarios: happy path, edge cases, error conditions, and invalid inputs. **Don't Do This:** Only test the happy path. This leaves your code vulnerable to unexpected errors. **Why:** Comprehensive test cases ensure the robustness of your code in various situations. ### 2.3. Assertions **Do This:** Use clear and concise assertions to verify expected outcomes. Tools like Jest, Chai, or Mocha provide a variety of assertion methods. **Don't Do This:** Write assertions that are too broad or vague. They provide little value in identifying specific issues. **Why:** Specific assertions make it easier to pinpoint the cause of test failures. ## 3. Integration Testing ### 3.1. Scope **Do This:** Integration test how different parts of your application work together, such as components interacting with APIs, databases, or external services. This is particularly relevant when using serverless functions on Vercel. **Don't Do This:** Test the entire application flow in an integration test. This belongs to end-to-end testing. **Why:** Integration tests verify that different modules are compatible and communicate correctly. They are crucial for catching issues that unit tests might miss. **Example:** """typescript // Example: Integration testing a component that fetches data from an API (using Jest and "nock" for mocking the API) // component.tsx import { useState, useEffect } from 'react'; async function fetchData() { const res = await fetch('/api/data'); // Assuming you have an API route on Vercel return await res.json(); } function MyComponent() { const [data, setData] = useState(null); useEffect(() => { fetchData().then(setData); }, []); if (!data) { return <p>Loading...</p>; } return <p>Data: {data.message}</p>; } export default MyComponent; // component.test.tsx import { render, screen, waitFor } from '@testing-library/react'; import MyComponent from './component'; import nock from 'nock'; describe('MyComponent', () => { it('should fetch and display data from the API', async () => { nock('http://localhost:3000') // Replace with your actual Vercel deployment URL if testing against a deployed API .get('/api/data') .reply(200, { message: 'Hello from API!' }); render(<MyComponent />); await waitFor(() => screen.getByText('Data: Hello from API!')); }); }); """ ### 3.2. Environment Variables **Do This:** Use environment variables to configure integration test environments. Vercel provides a mechanism for setting environment variables for different environments (development, preview, production). Use separate API keys or test databases for testing. **Don't Do This:** Hardcode sensitive information or API keys directly into your tests. **Why:** Environment variables allow you to customize test environments without modifying the code. Crucial given Vercel's environment-specific settings. ### 3.3. Vercel API Routes **Do This:** When testing Vercel API Routes, use "supertest" or "node-mocks-http" to simulate HTTP requests and responses. You can directly import your API route handler into your test file. **Don't Do This:** Make external API calls to deployed Vercel instances from your tests unless you are explicitly performing an end-to-end test. **Why:** This reduces test flakiness and allows full control over the API handler's execution. **Example:** """typescript // Example: Integration testing a Vercel API route (using Jest and supertest) // pages/api/hello.ts (Vercel API route) import { NextApiRequest, NextApiResponse } from 'next'; export default function handler(req: NextApiRequest, res: NextApiResponse) { res.status(200).json({ name: 'John Doe' }); } // pages/api/hello.test.ts import request from 'supertest'; import { createMocks } from 'node-mocks-http'; import handler from './hello'; // Import the API route handler describe('/api/hello', () => { it('should return a 200 status and the correct JSON response', async () => { const { req, res } = createMocks(); await handler(req, res); expect(res._getStatusCode()).toBe(200); expect(JSON.parse(res._getData())).toEqual({ name: 'John Doe' }); }); }); """ ## 4. End-to-End (E2E) Testing ### 4.1. Scope **Do This:** End-to-end test the complete user flow through your application, mimicking real user interactions. Focus on verifying that the application functions correctly as a whole, from the user interface to the backend services. Use tools like Cypress, Playwright, or Selenium. **Don't Do This:** Use E2E tests to verify fine-grained details that can be covered by unit or integration tests. **Why:** E2E tests provide the highest level of confidence that your application is working as expected but are also the slowest and most brittle. ### 4.2. Environment **Do This:** Run E2E tests against a staging or pre-production environment that closely mirrors your production environment on Vercel. This allows you to catch environment-specific issues. Leverage Vercel Preview deployments for this. **Don't Do This:** Run E2E tests against your local development environment or directly against your production environment. **Why:** Running E2E tests in a staging environment provides a realistic representation of production behavior. ### 4.3. Test Data **Do This:** Use a dedicated test data set for E2E testing to avoid corrupting your production data. Seed the test database before running tests and clean it up afterwards. **Don't Do This:** Use real user data or directly modify your production database during E2E testing. **Why:** Proper test data management prevents accidental data corruption and ensures consistency between tests. **Example:** """typescript // Example: E2E testing using Cypress // cypress/e2e/home.cy.js describe('Home Page', () => { it('displays the home page with a welcome message', () => { cy.visit('/'); // Assuming your application is running on the default baseUrl configured in cypress.config.js cy.contains('Welcome to My Application'); }); it('allows a user to navigate to the about page', () => { cy.visit('/'); cy.contains('About').click(); cy.url().should('include', '/about'); // Assuming the About page has a route of /about cy.contains('About Us'); }); }); // Cypress configuration (cypress.config.js) - crucial for Vercel deployment import { defineConfig } from 'cypress' export default defineConfig({ e2e: { baseUrl: 'http://localhost:3000', // Change if using Vercel preview deployment URL setupNodeEvents(on, config) { // implement node event listeners here }, }, }) """ ### 4.4 Vercel Deployment Context **Do This:** Leverage Vercel's environment variables within your E2E tests to dynamically configure test URLs and API endpoints. **Don't Do This:** Hardcode URLs, particularly production URLs, into your E2E tests. **Why:** Ensures that your E2E tests automatically adapt to different Vercel deployment environments (development, preview, production). ### 4.5 Visual Regression Testing **Do This**: Integrate visual regression testing into your E2E suite to catch unintended UI changes. Tools like Percy or Chromatic work well with Vercel previews. **Don't Do This**: Rely solely on functional E2E tests, as they may miss subtle visual regressions. **Why**: Ensures UI consistency across deployments and reduces the risk of shipping visual bugs to production. ## 5. Performance Testing ### 5.1. Load Testing **Do This:** Simulate realistic user traffic to assess the performance of your application under load. Tools like LoadView, k6 or Artillery can be used. This is particularly important for serverless functions on Vercel, as cold starts can impact performance. Pay close attention to Vercel functions execution duration and memory usage. **Don't Do This:** Deploy code to production without understanding its performance characteristics under load. **Why:** Load testing identifies performance bottlenecks and helps ensure that your application can handle expected traffic volumes. ### 5.2. Monitoring **Do This:** Integrate monitoring tools like Vercel Analytics, Datadog, or New Relic to track key performance metrics such as response time, error rate, and resource utilization. **Don't Do This:** Wait until users complain about performance issues before investigating. **Why:** Monitoring provides real-time insights into application performance and helps proactively identify and address issues. Vercel Analytics is particularly strong here. ### 5.3. Optimization **Do This:** Optimize your code, database queries, and infrastructure to improve performance. Consider using Vercel's Edge Functions or Image Optimization features. Implement caching strategies where appropriate. **Don't Do This:** Ignore performance issues until they become critical. **Why:** Performance optimization ensures a responsive and efficient user experience. ## 6. Security Testing ### 6.1. Vulnerability Scanning **Do This:** Use vulnerability scanning tools to identify potential security flaws in your code and dependencies. Tools like Snyk, SonarQube and GitHub Advanced Security are recommended. **Don't Do This:** Assume that your code is secure without performing security testing. **Why:** Vulnerability scanning helps prevent security breaches and protect sensitive data. ### 6.2. Input Validation **Do This:** Validate all user inputs to prevent injection attacks and other security vulnerabilities. Use libraries like "zod" or "joi" for robust input validation. **Don't Do This:** Trust user input without validation. **Why:** Proper input validation prevents malicious users from exploiting vulnerabilities in your application. ### 6.3. Authentication and Authorization **Do This:** Implement strong authentication and authorization mechanisms to protect access to sensitive resources. Consider using a third-party authentication provider like Auth0 or Clerk, especially seamless for Vercel. **Don't Do This:** Use weak passwords or store sensitive information in plain text. **Why:** Strong authentication and authorization prevent unauthorized access to your application and data. ### 6.4. Secrets Management **Do This:** Use environment variables or dedicated secrets management tools to store sensitive information such as API keys and database credentials. Avoid committing secrets to your code repository. Vercel provides a secure way to manage environment variables. **Don't Do This:** Hardcode secrets directly into your code. **Why:** Proper secrets management prevents accidental exposure of sensitive information. ## 7. Continuous Integration and Continuous Delivery (CI/CD) ### 7.1. Automated Testing in CI/CD **Do This:** Integrate all types of tests (unit, integration, E2E, performance, security) into your CI/CD pipeline. Use tools like GitHub Actions or Vercel's built-in CI/CD features. **Don't Do This:** Rely on manual testing or postpone testing until the end of the development cycle. **Why:** Automated testing in CI/CD ensures consistent and reliable testing, catching regressions early and reducing the risk of deploying faulty code. ### 7.2. Vercel Preview Deployments **Do This:** Leverage Vercel's preview deployments to automatically deploy your application for every pull request. Run tests against these preview deployments to catch issues before they are merged into the main branch. **Don't Do This:** Merge code without testing it in a realistic environment. **Why:** Preview deployments provide a safe and isolated environment for testing changes before they are deployed to production. ### 7.3. Rollbacks **Do This:** Implement a rollback mechanism to quickly revert to a previous version of your application in case of a critical issue. Vercel provides built-in rollback functionality. **Don't Do This:** Leave users stranded with a broken application. **Why:** Rollbacks minimize downtime and prevent data loss in case of unforeseen issues. By adhering to these testing methodology standards, development teams using Vercel can build high-quality, reliable, and secure applications that deliver a superior user experience. Continuous refinement of these standards, based on project experience and evolving Vercel capabilities, is highly encouraged.
# Core Architecture Standards for Vercel This document outlines the core architectural standards for developing applications on Vercel. It focuses on fundamental architectural patterns, optimal project structure, and organizational principles to ensure maintainability, performance, and security. These standards are tailored to the Vercel platform and promote the use of modern approaches and patterns. ## 1. Architectural Patterns Selecting the right architectural pattern is crucial for building scalable and maintainable Vercel applications. ### 1.1. Serverless Functions (API Routes) Leverage serverless functions for handling API endpoints and backend logic. Vercel's serverless functions allow you to execute code without managing servers, scaling automatically and minimizing operational overhead. **Do This:** * Use serverless functions for handling API requests, database interactions, and background tasks. **Don't Do This:** * Avoid running long-running processes directly within serverless functions. Use queues or background jobs for computationally intensive tasks. **Why:** Serverless functions ensure scalability, reduce costs by only charging for actual usage, and simplify deployment management. **Code Example (Next.js API Route):** """javascript // pages/api/hello.js export default async function handler(req, res) { if (req.method === 'GET') { res.status(200).json({ message: 'Hello from Vercel!' }); } else { res.status(405).json({ message: 'Method Not Allowed' }); } } """ **Anti-Pattern:** Placing heavy computation directly inside a serverless function without considering timeouts or performance implications. ### 1.2. Jamstack Architecture Jamstack (JavaScript, APIs, and Markup) is an architectural approach that emphasizes pre-rendering, decoupling, and leveraging edge caching. **Do This:** * Pre-render as much content as possible during build time using Static Site Generation (SSG) or Incremental Static Regeneration (ISR). * Use a Content Delivery Network (CDN) for serving static assets. Vercel's Edge Network is ideal for this. * Decouple the frontend from the backend using APIs, allowing for greater flexibility and scalability. **Don't Do This:** * Rely on server-side rendering (SSR) for content that can be pre-rendered. * Bypass the CDN and directly serve static assets from the origin server. **Why:** Jamstack enhances performance, improves security by minimizing the attack surface, and simplifies deployments. **Code Example (Next.js SSG):** """javascript // pages/index.js export async function getStaticProps() { // Fetch data from an API or CMS const data = await fetchData(); return { props: { data, }, }; } export default function Home({ data }) { return ( <h1>{data.title}</h1> <p>{data.content}</p> ); } """ **Code Example (Next.js ISR):** """javascript // pages/blog/[slug].js export async function getStaticPaths() { // Get all possible paths based on external data const posts = await fetchPosts(); const paths = posts.map((post) => ({ params: { slug: post.slug }, })); return { paths, fallback: 'blocking', // or true for incremental generation }; } export async function getStaticProps({ params }) { // Fetch data for the specific slug const post = await fetchPost(params.slug); return { props: { post, }, revalidate: 60, // Revalidate every 60 seconds }; } export default function Post({ post }) { return ( <h1>{post.title}</h1> <p>{post.content}</p> ); } """ **Anti-Pattern:** Using SSR for content that is rarely updated, leading to unnecessary server load and slower page loads. ### 1.3. Microservices (Polyrepo vs. Monorepo using Vercel Projects) When the application grows in complexity, consider a microservices architecture which Vercel can support effectively through multiple "Vercel Projects" pointing at different repositories. **Do This:** * If choosing microservices, design services with clear boundaries and single responsibilities. * Consider the trade-offs between a polyrepo (multiple repositories, one per service) and a monorepo (single repository for all services) approach. Vercel Projects work equally well for either, but the organization and deployment strategies differ. * Use a service mesh or API gateway for managing communication between services. * Set up separate Vercel projects for each microservice to manage deployments and scaling independently. **Don't Do This:** * Create overly granular microservices that introduce unnecessary complexity. * Share database schemas or business logic between services without careful consideration. **Why:** Microservices improve scalability, fault isolation, and team autonomy. Vercel's project structure allows for easy management of multiple services. **Polyrepo Example:** * "repo-auth-service" Vercel Project -> connects to auth-service repo * "repo-user-service" Vercel Project -> connects to user-service repo * "repo-product-service" Vercel Project -> connects to product-service repo **Monorepo Example:** * "main-repo" Vercel Project (configure the settings to only deploy changed directories) **Anti-Pattern:** Creating a "distributed monolith" where services are tightly coupled and dependent on each other. ### 1.4 Edge Functions Utilize Vercel Edge Functions for tasks that require low latency and high performance, like personalization and geolocation. **Do This:** * Implement Edge Functions for tasks like A/B testing, authentication, and request rewriting, leveraging geographical proximity to the user. * Cache responses at the edge to reduce latency and improve performance. **Don't Do This:** * Execute complex business logic or database operations within Edge Functions due to limitations on execution time and resources. * Overuse Edge Functions for tasks that can be efficiently handled by serverless functions. **Why:** Edge Functions provide low latency, improved performance, and scalability at the edge of the network. **Code Example (Edge Function):** """typescript // middleware.ts (Vercel's Edge Middleware) import { NextResponse } from 'next/server' import type { NextRequest } from 'next/server' export function middleware(request: NextRequest) { const country = request.geo?.country || 'US' if (country === 'DE') { return NextResponse.rewrite(new URL('/de/homepage', request.url)) } return NextResponse.next() } export const config = { matcher: '/', } """ **Anti-Pattern:** Using Edge Functions for operations that require significant computation or database access results in slow performance and potential timeouts. ## 2. Project Structure and Organization A well-defined project structure is essential for maintainability and collaboration. ### 2.1. Monorepo vs. Polyrepo Choose a project structure that aligns with your organization's size, complexity, and development workflow. **Do This:** * For small to medium-sized projects, start with a monorepo for simplicity using tools like Turborepo or Nx. * For large, complex applications with multiple teams, consider a polyrepo structure for greater autonomy and scalability. **Don't Do This:** * Switch between monorepo and polyrepo without carefully considering the implications on tooling, deployment, and collaboration. * Create a deeply nested directory structure within the repository. **Why:** A well-organized project structure improves code discoverability, simplifies dependency management, and promotes code reuse. **Monorepo Example (using Turborepo):** """ my-monorepo/ apps/ web/ # Next.js frontend docs/ # Documentation website packages/ ui/ # UI component library utils/ # Utility functions turbo.json # Turborepo configuration package.json # Root package.json """ **Anti-Pattern:** A disorganized repository structure that makes it difficult to find and understand code. ### 2.2. Directory Structure for Next.js Projects Organize Next.js projects using a structure that promotes separation of concerns and maintainability. **Do This:** * Use the "pages" directory for defining routes and API endpoints. * Create a "components" directory for reusable UI components. * Store utility functions and helper modules in a "utils" or "lib" directory. * Use an "assets" directory for static assets like images and fonts. * Create a "styles" directory for CSS modules, global stylesheets, and theme definitions. **Don't Do This:** * Place all code within the "pages" directory, leading to a monolithic structure. * Mix components, utilities, and styles in the same directory. **Why:** A standardized directory structure enables clear separation of concerns, simplifies navigation, and promotes code reuse. **Code Example (Directory Structure):** """ my-next-app/ pages/ api/ hello.js # API endpoint index.js # Home page about.js # About page components/ Button.js # Reusable button component Header.js # Header component utils/ api.js # API client functions formatDate.js # Date formatting utility assets/ logo.png # Logo image styles/ globals.css # Global styles Button.module.css # CSS Modules for Button component """ **Anti-Pattern:** A flat directory structure that mixes different types of files, making it difficult and time-consuming to find specific code. ### 2.3. Component-Based Architecture Design the application using reusable and composable components. **Do This:** * Break down the UI into smaller components with single responsibilities. * Use prop types or TypeScript for defining component interfaces. * Create a component library for reusable UI elements. **Don't Do This:** * Create large, monolithic components that are difficult to maintain and reuse. * Duplicate code across multiple components. **Why:** A component-based architecture promotes code reuse, simplifies maintenance, and improves testability. **Code Example (React Component):** """jsx // components/Button.js import React from 'react'; import styles from './Button.module.css'; import PropTypes from 'prop-types'; function Button({ children, onClick, className }) { return ( {children} ); } Button.propTypes = { children: PropTypes.node.isRequired, onClick: PropTypes.func, className: PropTypes.string, }; export default Button; """ **Anti-Pattern:** Duplicating code across multiple components instead of creating a reusable component. ## 3. Code Organization Principles ### 3.1. Separation of Concerns Divide the code into distinct sections, each addressing a separate concern. **Do This:** * Separate concerns for UI rendering, data fetching, business logic, and data storage. * Use custom hooks to encapsulate reusable logic. **Don't Do This:** * Mix UI rendering, data fetching, and business logic in the same component. **Why:** Separation of concerns improves code maintainability, testability, and readability. **Code Example (Custom Hook):** """javascript // hooks/useFetch.js import { useState, useEffect } from 'react'; function useFetch(url) { const [data, setData] = useState(null); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); useEffect(() => { async function fetchData() { try { const response = await fetch(url); const json = await response.json(); setData(json); } catch (e) { setError(e); } finally { setLoading(false); } } fetchData(); }, [url]); return { data, loading, error }; } export default useFetch; // pages/index.js import useFetch from '../hooks/useFetch'; function Home() { const { data, loading, error } = useFetch('/api/data'); if (loading) return <p>Loading...</p>; if (error) return <p>Error: {error.message}</p>; return ( <h1>Data from API</h1> <pre>{JSON.stringify(data, null, 2)}</pre> ); } export default Home; """ **Anti-Pattern:** A component that performs UI rendering, data fetching, and data manipulation, making it difficult to understand and maintain. ### 3.2. Single Responsibility Principle (SRP) Ensure that each class, function, or component has only one reason to change. **Do This:** * Design functions and components that perform a single, well-defined task. * Avoid creating functions or classes that are responsible for multiple unrelated tasks. **Don't Do This:** * Create a utility function that can format dates, validate emails, and trim strings, violating SRP. **Why:** SRP improves code clarity, testability, and reduces the risk of introducing bugs when modifying code. **Code Example (SRP Compliant Function):** """javascript // utils/formatDate.js function formatDate(date) { return new Date(date).toLocaleDateString(); } export default formatDate; // utils/validateEmail.js function validateEmail(email) { const regex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; return regex.test(email); } export default validateEmail; """ **Anti-Pattern:** A lengthy function that could be broken down into smaller, more manageable functions, violating SRP. ### 3.3. Convention over Configuration Adopt a common set of conventions for naming, directory structure, and coding style. Vercel / Next.js encourages this, but you can codify more specific rules. **Do This:** * Use consistent naming conventions for variables, functions, and components. * Follow a standardized directory structure for organizing project files. * Use a configuration management system to centralize and manage application settings. **Don't Do This:** * Inconsistent naming conventions that make it difficult to understand the purpose of a variable or function. **Why:** Convention over configuration reduces cognitive load, improves code consistency, and simplifies onboarding for new developers. **Technology-Specific Details:** * Leverage Vercel's environment variables for configuring different deployment environments. * Use Vercel's analytics and logging to monitor application performance and identify potential issues. * Use Next.js's built-in features for routing, API endpoints, and data fetching. By adhering to these core architectural standards, you can build robust, scalable, and maintainable applications on Vercel. These standards are intended to provide a foundation for creating high-quality code that is easy to understand, test, and extend.
# State Management Standards for Vercel This document outlines the coding standards for state management in Vercel applications. It aims to provide clear guidelines for developers to build maintainable, performant, and secure applications that leverage the benefits of the Vercel platform. It is designed to be used by developers and serve as context for AI coding assistants. ## 1. Introduction to State Management on Vercel ### 1.1. State in Serverless Environments Vercel is designed around serverless functions, which are stateless by nature. This means each function invocation is independent and doesn't retain data between calls. Therefore, effective state management is crucial for building dynamic and interactive applications. ### 1.2. Scope of this Document This document focuses on state management strategies relevant in the context of Vercel, covering both client-side and server-side considerations. We'll discuss techniques for managing UI state, data fetching, caching, and persistence, taking advantage of Vercel's features and integrations where possible. The latest version of Vercel is considered when defining standards. ## 2. Client-Side State Management ### 2.1. Choosing a Framework React is the most common framework used with Vercel. While this document isn't React-specific, many examples implicitly use React as the UI layer. Consider these common solutions: * **useState and useContext (React):** Suitable for simple state management within components and sharing state across component trees. * **Redux:** A predictable state container for managing complex application state. * **Zustand:** A small, fast, and scalable bearbones state-management solution using simplified flux principles. * **Recoil:** An experimental state management library from Facebook for React apps. * **Jotai:** Primitive and flexible state management for React with an atomic model. * **TanStack Query (formerly React Query):** For managing, caching, and updating asynchronous data in your React components. * **SWR (Stale-While-Revalidate):** A React Hooks library for remote data fetching. Plays especially well with Vercel's edge network. **Standard:** Choose the state management solution that best fits the complexity and scale of your application. For simple applications, "useState" and "useContext" may be sufficient. For larger applications, consider Redux, Zustand, Recoil, Jotai, TanStack Query, or SWR. **WHY:** Selecting the right tool simplifies development, improves maintainability, and optimizes performance. Avoid over-engineering state management for small applications. ### 2.2. React useState and useContext #### 2.2.1 Standards * **Do This:** Use "useState" for managing local component state. * **Do This:** Use "useContext" for sharing state between components without prop drilling. Combine with a reducer ("useReducer") for complex state updates. * **Don't Do This:** Overuse "useState" for global application state. This makes state difficult to manage and debug. * **Don't Do This:** Mutate state directly. Always use the setter function provided by "useState" or dispatch an action to a reducer. #### 2.2.2 Code Examples """javascript // Example using useState import React, { useState } from 'react'; function Counter() { const [count, setCount] = useState(0); return ( <div> <p>Count: {count}</p> <button onClick={() => setCount(count + 1)}>Increment</button> </div> ); } export default Counter; // Example using useContext with useReducer import React, { createContext, useReducer, useContext } from 'react'; const initialState = { count: 0 }; const reducer = (state, action) => { switch (action.type) { case 'increment': return { count: state.count + 1 }; case 'decrement': return { count: state.count - 1 }; default: return state; } }; const CounterContext = createContext(); function CounterProvider({ children }) { const [state, dispatch] = useReducer(reducer, initialState); return ( <CounterContext.Provider value={{ state, dispatch }}> {children} </CounterContext.Provider> ); } function useCounter() { return useContext(CounterContext); } function CounterDisplay() { const { state } = useCounter(); return <p>Count: {state.count}</p>; } function CounterButtons() { const { dispatch } = useCounter(); return ( <div> <button onClick={() => dispatch({ type: 'increment' })}>Increment</button> <button onClick={() => dispatch({ type: 'decrement' })}>Decrement</button> </div> ); } function App() { return ( <CounterProvider> <CounterDisplay /> <CounterButtons /> </CounterProvider> ); } export default App; """ **WHY:** Proper usage of "useState" and "useContext" ensures component re-renders only when necessary, optimizing performance. Using "useReducer" with "useContext" centralizes state logic, improving maintainability and predictability. ### 2.3. Redux #### 2.3.1 Standards * **Do This:** Use Redux for managing complex application state that needs to be accessible from multiple components. * **Do This:** Follow the Redux principles of a single source of truth, state is read-only, and changes are made with pure functions. * **Do This:** Use Redux Toolkit to simplify Redux setup and reduce boilerplate code. Redux Toolkit provides "configureStore", "createSlice", and other utilities to make Redux easier to use. This is now the *recommended* approach. * **Don't Do This:** Mutate state directly in reducers. Always return a new state object. Use libraries like Immer to simplify immutable updates. * **Don't Do This:** Store derived data in the Redux store. Instead, calculate derived data using selectors. * **Don't Do This:** Over-rely on Redux for simple applications. Context or Zustand might be more appropriate. #### 2.3.2 Code Example """javascript // Example using Redux Toolkit import { configureStore, createSlice } from '@reduxjs/toolkit'; import { Provider, useDispatch, useSelector } from 'react-redux'; // Define a slice const counterSlice = createSlice({ name: 'counter', initialState: { value: 0, }, reducers: { increment: (state) => { state.value += 1; }, decrement: (state) => { state.value -= 1; }, incrementByAmount: (state, action) => { state.value += action.payload; }, }, }); // Export actions export const { increment, decrement, incrementByAmount } = counterSlice.actions; // Configure the store const store = configureStore({ reducer: { counter: counterSlice.reducer, }, }); // React components function Counter() { const count = useSelector((state) => state.counter.value); const dispatch = useDispatch(); return ( <div> <p>Count: {count}</p> <button onClick={() => dispatch(increment())}>Increment</button> <button onClick={() => dispatch(decrement())}>Decrement</button> <button onClick={() => dispatch(incrementByAmount(5))}>Increment by 5</button> </div> ); } function App() { return ( <Provider store={store}> <Counter /> </Provider> ); } export default App; """ **WHY:** Redux provides a centralized and predictable state management solution. Redux Toolkit simplifies Redux setup and reduces boilerplate, leading to more maintainable code. ### 2.4. Zustand #### 2.4.1 Standards * **Do This:** Use Zustand for a simple and unopionated state management solution * **Do This:** Define a clear, explicit data flow using setter methods within the Zustand store. * **Don't Do This:** Mutate the state directly. * **Don't Do This:** Create large and complex stores if the app is small. #### 2.4.2 Code Example """javascript import create from 'zustand' const useStore = create(set => ({ bears: 0, increasePopulation: () => set(state => ({ bears: state.bears + 1 })), removeAllBears: () => set({ bears: 0 }) })) function BearCounter() { const bears = useStore(state => state.bears) return <h1>{bears} around here ...</h1> } function BearApp() { return ( <> <BearCounter /> <Buttons /> </> ); } function Buttons() { const increasePopulation = useStore(state => state.increasePopulation) const removeAllBears = useStore(state => state.removeAllBears) return ( <> <button onClick={increasePopulation}>one up</button> <button onClick={removeAllBears}>remove all</button> </> ) } """ **WHY:** Zustand is small with a minimal API, and avoids the boilerplate needed in Redux. ### 2.5. Data Fetching and Caching with SWR or TanStack Query #### 2.5.1 Standards * **Do This:** Use SWR or TanStack Query for fetching, caching, and updating remote data. * **Do This:** Leverage the built-in caching mechanisms of SWR/TanStack Query to reduce network requests and improve performance. * **Do This:** Use optimistic updates to provide a better user experience. * **Do This:** Use "useSWRConfig" in SWR or the QueryClient in TanStack Query to manage cache invalidation and refetching data when necessary. * **Don't Do This:** Manually implement caching logic when using SWR/TanStack Query. * **Don't Do This:** Over-fetch data. Request only the data that is needed. #### 2.5.2 Code Examples """javascript // Example using SWR import useSWR from 'swr'; const fetcher = (...args) => fetch(...args).then(res => res.json()); function Profile() { const { data, error } = useSWR('/api/user', fetcher); if (error) return <div>failed to load</div>; if (!data) return <div>loading...</div>; return ( <div> <h1>{data.name}</h1> <p>{data.bio}</p> </div> ); } export default Profile; // Example using TanStack Query import { useQuery } from '@tanstack/react-query'; const fetcher = async (key) => { const res = await fetch(key); return res.json(); }; function Profile() { const { isLoading, error, data } = useQuery(['user'], () => fetcher('/api/user')); if (isLoading) return <div>Loading...</div>; if (error) return <div>An error has occurred: {error.message}</div>; return ( <div> <h1>{data.name}</h1> <p>{data.bio}</p> </div> ); } export default Profile; """ **WHY:** SWR and TanStack Query simplify data fetching and caching, reduce boilerplate code, and improve performance by leveraging caching and revalidation strategies. SWR also integrates well with Vercel Edge Functions and ISR (Incremental Static Regeneration) for optimal performance. ## 3. Server-Side State Management ### 3.1. Vercel Edge Functions #### 3.1.1 Standards * **Do This:** Use Vercel Edge Functions for handling dynamic requests and implementing server-side logic. * **Do This:** Cache data in Edge Functions to reduce latency and improve performance, especially for frequently accessed data. * **Do This:** Utilize Vercel's Global Edge Network for low-latency data access. * **Don't Do This:** Perform heavy computations or database queries directly in Edge Functions, as they have limited execution time. Offload these tasks to backend services or databases. * **Don't Do This:** Store sensitive data directly in Edge Functions code. Use environment variables or secrets management. #### 3.1.2 Code Example """javascript // Vercel Edge Function example export const config = { runtime: 'edge', }; export default async function handler(req) { // Example of caching data (using a simple in-memory cache) if (handler.cache === undefined) { handler.cache = { data: null, timestamp: 0, }; } const now = Date.now(); const cacheExpiry = 60 * 1000; // 60 seconds if (handler.cache.data === null || now - handler.cache.timestamp > cacheExpiry) { // Fetch data from an external API const res = await fetch('https://api.example.com/data'); const data = await res.json(); // Update the cache handler.cache = { data: data, timestamp: now, }; } return new Response( JSON.stringify(handler.cache.data), { status: 200, headers: { 'content-type': 'application/json', 'cache-control': 'public, max-age=60', // Cache at the edge }, } ); } """ **WHY:** Vercel Edge Functions provide a fast and scalable environment for handling dynamic requests. Caching data at the edge reduces latency and improves performance, leading to a better user experience. ### 3.2. Databases #### 3.2.1 Standards * **Do This:** Choose a database that fits the needs of your application, considering factors like data structure, scalability, and cost. * **Do This:** Use connection pooling to efficiently manage database connections. * **Do This:** Use parameterized queries to prevent SQL injection attacks. * **Don't Do This:** Store sensitive information in plain text. Use encryption to protect sensitive data. * **Don't Do This:** Expose database credentials directly in your code. Use environment variables. #### 3.2.2 Code Example Example with Vercel Postgres and "pg" library: """javascript // Example using Vercel Postgres (Neon) import { Pool } from 'pg'; const pool = new Pool({ connectionString: process.env.POSTGRES_URL + "?sslmode=require", }); export default async function handler(req, res) { try { const client = await pool.connect(); const result = await client.query('SELECT NOW()'); const now = result.rows[0].now; client.release(); res.status(200).json({ now }); } catch (error) { console.error('Error fetching data:', error); res.status(500).json({ error: 'Failed to fetch data' }); } } export const config = { api: { region: 'iad1', // Or other region }, }; """ **WHY:** Proper database management ensures data integrity, security, and scalability. ### 3.3. Caching Strategies (ISR, SSR, Edge Caching) #### 3.3.1 Standards * **Do This:** Use Incremental Static Regeneration (ISR) for content that is updated frequently but doesn't require real-time updates. * **Do This:** Use Server-Side Rendering (SSR) for content that must be dynamically generated on each request. * **Do This:** Leverage Vercel's Edge Network for caching static assets and API responses. * **Do This:** Configure appropriate "cache-control" headers to specify how long data should be cached. * **Don't Do This:** Over-use SSR, as it can negatively impact performance. Consider ISR as an alternative. * **Don't Do This:** Cache sensitive data or data that should not be shared. #### 3.3.2 Code Example """javascript // Example using ISR in Next.js (Vercel) export async function getStaticProps() { const res = await fetch('https://api.example.com/data'); const data = await res.json(); return { props: { data, }, revalidate: 60, // Regenerate every 60 seconds }; } function Page({ data }) { return ( <div> <h1>Data from API</h1> <pre>{JSON.stringify(data, null, 2)}</pre> </div> ); } export default Page; """ **WHY:** Effective caching strategies reduce server load, improve performance, and provide a better user experience. ISR allows for static generation with dynamic updates, balancing performance and freshness. ## 4. Optimizing for Performance ### 4.1. State Management Libraries When selecting the correct framework and method, make sure you take into account how it will function within Vercel, and it's performance. * **Avoid Memory Leaks:** Ensure components and state updates are handled properly to release memory efficiently. * **Optimize Re-renders:** Choose modern methods such as "useMemo" and "useCallback" to render components only when truly needed. ### 4.2. Data Transfer * **Optimize API Responses:** Compress API responses using gzip or Brotli compression to reduce data transfer sizes. * **Use GraphQL:** Consider using GraphQL to fetch only the data that is needed, reducing over-fetching. ### 4.3. Lazy Loading * **Implement Lazy Loading:** Load components and data only when they are needed to reduce initial load time. * **Code Splitting:** Split your code into smaller chunks to improve initial load time. ## 5. Security Considerations ### 5.1. Protecting Sensitive Data * **Environment Variables:** Store sensitive data such as API keys and database credentials in environment variables. * **Secrets Management:** Use a secrets management tool such as HashiCorp Vault to securely store and manage secrets. * **Encryption:** Encrypt sensitive data at rest and in transit. ### 5.2. Preventing Injection Attacks * **Parameterized Queries:** Use parameterized queries to prevent SQL injection attacks. * **Input Validation:** Validate all user inputs to prevent cross-site scripting (XSS) and other injection attacks. ### 5.3. Authentication and Authorization * **Implement Authentication:** Verify the identity of users before granting access to protected resources. * **Implement Authorization:** Control access to resources based on user roles and permissions. ## 6. Conclusion These coding standards provide a foundation for building robust, performant, secure, and maintainable Vercel applications. By adhering to these guidelines, development teams can ensure consistency, reduce errors, and optimize the overall development process. Remember to adapt and refine these standards based on the specific needs of your project and the evolving landscape of Vercel's capabilities. Regular review and updates to these standards are essential to keep pace with the latest best practices and advancements in the Vercel ecosystem.
# Security Best Practices Standards for Vercel This document outlines security best practices for developing and deploying applications on the Vercel platform. Adhering to these standards will reduce your application's risk profile, leading to more reliable and secure deployments. ## 1. Input Validation & Sanitization ### 1.1 Standard * **Do This:** Always validate and sanitize user inputs, regardless of origin (e.g., forms, query parameters, cookies, API calls, environment variables). Implement validation on both the client (for immediate feedback) and the server (for ultimate security). * **Don't Do This:** Trust user input implicitly. Never directly use unsanitized user input in database queries, shell commands, or rendered HTML. ### 1.2 Why * **Security:** Prevents various injection attacks (SQL injection, XSS, command injection) by ensuring data conforms to expected formats and removing potentially malicious content. It also helps to prevent Denial of Service (DoS) attacks. * **Maintainability:** Makes code more robust and predictable, as it handles unexpected input gracefully. * **Performance:** Rejecting invalid input early reduces unnecessary processing and resource consumption. ### 1.3 Code Examples #### 1.3.1 Server-Side Input Validation (Next.js API Route) """typescript // pages/api/submit-form.ts import { NextApiRequest, NextApiResponse } from 'next'; import validator from 'validator'; // Using a popular validator library export default async function handler(req: NextApiRequest, res: NextApiResponse) { if (req.method !== 'POST') { return res.status(405).end(); // Method Not Allowed } const { name, email, message } = req.body; // Input validation if (!name || !validator.isLength(name, { min: 2, max: 100 })) { return res.status(400).json({ error: 'Name must be between 2 and 100 characters.' }); } if (!email || !validator.isEmail(email)) { return res.status(400).json({ error: 'Invalid email address.' }); } if (!message || !validator.isLength(message, { min: 10, max: 500 })) { return res.status(400).json({ error: 'Message must be between 10 and 500 characters.' }); } // Sanitize the input const sanitizedName = validator.escape(name); const sanitizedEmail = validator.normalizeEmail(email); const sanitizedMessage = validator.escape(message); // Process the sanitized data (e.g., store in database, send email) try { //Simulate database interaction to handle the submission // normally you would interact with your chosen DB console.log({ name: sanitizedName, email: sanitizedEmail, message: sanitizedMessage }) res.status(200).json({ success: true }); } catch (error) { console.error('Error processing form submission:', error); res.status(500).json({ error: 'Internal server error' }); } } """ * **Explanation:** * The code uses the "validator" library for robust and standardized validation. Install it with "npm install validator". * It checks data presence and length constraints. Specifically it checks that the "name" is between 2 and 100 characters, email is a valid email, and message is between 10 and 500 characters. * Uses "validator.escape()" to prevent XSS attacks by escaping HTML entities. * Uses "validator.normalizeEmail()" to standardize email addresses. * Error handling is included to gracefully handle unexpected errors. #### 1.3.2 Client-Side Input Validation (React Component) """jsx // components/ContactForm.tsx import React, { useState } from 'react'; const EMAIL_REGEX = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; const ContactForm = () => { const [name, setName] = useState(''); const [email, setEmail] = useState(''); const [message, setMessage] = useState(''); const [errors, setErrors] = useState({name: '', email: '', message: ''}); const validateForm = () => { let isValid = true; let newErrors = {name: '', email: '', message: ''}; if(name.length < 2 || name.length > 100){ newErrors.name = 'Name must be between 2 and 100 characters.'; isValid = false; } if(!EMAIL_REGEX.test(email)){ newErrors.email = 'Invalid email address.'; isValid = false; } if(message.length < 10 || message.length > 500){ newErrors.message = 'Message must be between 10 and 500 characters.'; isValid = false; } setErrors(newErrors); return isValid; } const handleSubmit = async (e: React.FormEvent) => { e.preventDefault(); if (!validateForm()) { return; } try { const response = await fetch('/api/submit-form', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ name, email, message }), }); if (response.ok) { alert('Form submitted successfully!'); } else { alert('Form submission failed.'); } } catch (error) { console.error('Error submitting form:', error); alert('An error occurred while submitting the form.'); } }; return ( <form onSubmit={handleSubmit}> <div> <label htmlFor="name">Name:</label> <input type="text" id="name" value={name} onChange={(e) => setName(e.target.value)} /> {errors.name && <p className="error">{errors.name}</p>} </div> <div> <label htmlFor="email">Email:</label> <input type="email" id="email" value={email} onChange={(e) => setEmail(e.target.value)} /> {errors.email && <p className="error">{errors.email}</p>} </div> <div> <label htmlFor="message">Message:</label> <textarea id="message" value={message} onChange={(e) => setMessage(e.target.value)} /> {errors.message && <p className="error">{errors.message}</p>} </div> <button type="submit">Submit</button> </form> ); }; export default ContactForm; """ * **Explanation:** * This client-side validation provides immediate feedback to the user. * Built in regex variable called "EMAIL_REGEX" used to check structure of email. * The "validateForm" function checks for empty fields and invalid email formats. * Displays error messages to the user if validation fails. * Important: Client-side validation is *not* a substitute for server-side validation. ### 1.4 Anti-Patterns * **Blindly trusting environment variables:** While they are generally considered safe, validate environment variables received from external sources or user-provided configurations. Malicious actors could set unexpected environment variables to exploit vulnerabilities. * **Relying solely on client-side validation:** Always perform server-side validation, as client-side validation can be bypassed. * **Using insecure functions:** Avoid functions that are known to be vulnerable to injection attacks (e.g., "eval()" in JavaScript, "shell_exec()" in PHP) ## 2. Authentication and Authorization ### 2.1 Standard * **Do This:** Implement robust authentication and authorization mechanisms to protect sensitive resources. Use industry-standard protocols like OAuth 2.0 or OpenID Connect for authentication. Implement role-based access control (RBAC) to authorize users based on their roles and permissions. * **Don't Do This:** Store passwords in plain text. Implement your own custom authentication system without proper security expertise. Grant excessive privileges to users. ### 2.2 Why * **Security:** Protects sensitive data and functionality from unauthorized access. * **Maintainability:** Standardized authentication and authorization systems are easier to manage and audit. * **Compliance:** Many regulations require strong authentication and authorization controls. ### 2.3 Code Examples #### 2.3.1 Authentication with NextAuth.js (Recommended) """typescript // pages/api/auth/[...nextauth].ts import NextAuth from "next-auth" import GithubProvider from "next-auth/providers/github" import { PrismaAdapter } from "@next-auth/prisma-adapter" import { PrismaClient } from "@prisma/client" const prisma = new PrismaClient() export const authOptions = { adapter: PrismaAdapter(prisma), providers: [ GithubProvider({ clientId: process.env.GITHUB_ID, clientSecret: process.env.GITHUB_SECRET, }), ], session: { strategy: "jwt" }, jwt: { secret: process.env.JWT_SECRET }, callbacks: { async session({ session, token, user }) { return { ...session, user: { ...session.user, id: user.id } } }, }, secret: process.env.NEXTAUTH_SECRET, } export default NextAuth(authOptions) """ * **Explanation:** * This example uses NextAuth.js, a popular library for adding authentication to Next.js applications. * It uses the GitHub provider for social login. You can configure other providers as well (Google, Facebook, etc.). * It uses "PrismaAdapter" from "@next-auth/prisma-adapter" and "@prisma/client" to persist user data in a database so you will need to install these dependencies. "npm install next-auth @next-auth/prisma-adapter @prisma/client" * The code above includes a "jwt" section to add JWT secret. * The "callbacks" section extends the session object with the user ID. This is critical for authorization logic. * Protect API routes with "getSession" from "next-auth/react". #### 2.3.2 Protecting API Routes with "getSession" """typescript // pages/api/protected.ts import { NextApiRequest, NextApiResponse } from 'next'; import { getSession } from 'next-auth/react'; export default async function handler(req: NextApiRequest, res: NextApiResponse) { const session = await getSession({ req }); if (!session) { return res.status(401).json({ error: 'Unauthorized' }); } // Access the user id from the session const userId = session.user.id; // Your protected logic here using the user Id extracted from the session. res.status(200).json({ message: "Hello ${session.user.name}, your user id is ${userId}" }); } """ * The session contains user information if properly authenticated. #### 2.3.3 Role-Based Access Control (RBAC) """typescript // middleware.ts import { NextResponse } from 'next/server' import type { NextRequest } from 'next/server' // Role list const roles = { admin: ['/admin'], employee: ['/employee', '/settings'], user: ['/settings'] } // Matching Paths in Regex const protectedRoutes = ['/admin', '/employee', '/settings']; const publicRoutes = ['/login', '/register'] export function middleware(req: NextRequest) { const session = req.cookies.get('next-auth.session-token'); const isProtectedRoute = protectedRoutes.some((path) => req.nextUrl.pathname.startsWith(path)) if (!session && isProtectedRoute) { return NextResponse.redirect(new URL('/login', req.url)) } // User roles and paths - Database driven const role = 'employee'; //Should come from database if (roles[role] && roles[role].some((path) => req.nextUrl.pathname.startsWith(path))) { return NextResponse.next(); } return NextResponse.redirect(new URL('/', req.url)); } // See "Matching Paths" below to learn more export const config = { matcher: ['/employee/:path*', '/admin/:path*', '/settings/:path*'] } """ * **Explanation:** * This uses Next.js middleware. * It redirects unauthenticated users to the login page. * Determines user role and compares to allowed routes on the "roles" object. * **Important:** Access user roles from a database or secure store. *Do not* hardcode roles in middleware. This demonstration is over-simplified for illustrative purposes. ### 2.4 Anti-Patterns * **Storing sensitive information in cookies:** Avoid storing sensitive data (e.g., passwords, API keys) in cookies. If you must store sensitive data in cookies, encrypt it properly and set appropriate flags (e.g., "HttpOnly", "Secure", "SameSite"). * **Using weak password hashing algorithms:** Always use strong and modern password hashing algorithms like bcrypt, Argon2, or scrypt. * **Implementing "remember me" functionality insecurely:** Use secure tokens with expiration dates and rotate them regularly. Do not simply store usernames and passwords in cookies. * **CORS misconfiguration:** Incorrect CORS settings can expose your API to cross-site request forgery attacks. Carefully configure allowed origins. ## 3. Data Protection ### 3.1 Standard * **Do This:** Encrypt sensitive data at rest and in transit. Use HTTPS for all communication. Store encryption keys securely. Implement proper data masking techniques if showing data in UI. * **Don't Do This:** Store sensitive data in plain text. Use outdated or weak encryption algorithms. Expose sensitive data in logs or error messages. ### 3.2 Why * **Security:** Protects data from unauthorized access and disclosure. Complies with regulations. * **Maintainability:** Centralized encryption and key management simplifies security maintenance. * **Compliance:** Many regulations (e.g., GDPR, HIPAA) require data protection measures. ### 3.3 Code Examples #### 3.3.1 Data Encryption with "crypto" Module (Node.js API Route) """typescript // pages/api/encrypt.ts import { NextApiRequest, NextApiResponse } from 'next'; import crypto from 'crypto'; const algorithm = 'aes-256-cbc'; // Use a strong encryption algorithm const key = crypto.randomBytes(32); // 32 bytes = 256 bits const iv = crypto.randomBytes(16); // 16 bytes = 128 bits export default async function handler(req: NextApiRequest, res: NextApiResponse) { if (req.method !== 'POST') { return res.status(405).end(); } const { data } = req.body; if (!data) { return res.status(400).json({ error: 'Data is required.' }); } // Encryption const cipher = crypto.createCipheriv(algorithm, Buffer.from(key), iv); let encrypted = cipher.update(data); encrypted = Buffer.concat([encrypted, cipher.final()]); const encryptedData = { iv: iv.toString('hex'), encryptedData: encrypted.toString('hex') }; // Decryption (for demonstration purposes within the same endpoint) const decipher = crypto.createDecipheriv(algorithm, Buffer.from(key), Buffer.from(encryptedData.iv, 'hex')); let decrypted = decipher.update(Buffer.from(encryptedData.encryptedData, 'hex')); decrypted = Buffer.concat([decrypted, decipher.final()]); // Securely store the key and IV. Do *NOT* hardcode or expose them. // In a real-world scenario, you'd store these in a secure key vault console.log('Encryption Key (keep secret!):', key.toString('hex')); console.log('Initialization Vector (keep secret!):', iv.toString('hex')); res.status(200).json({ encryptedData, decryptedData: decrypted.toString() }); } """ * **Explanation:** * Uses the "crypto" module for symmetric encryption. Specifically, "aes-256-cbc" is recommended for its strength. Other modern and well-vetted algorithms are also appropriate. * Generates a random encryption key and initialization vector (IV) for each encryption operation. *Important:* **Never** hardcode encryption keys or IVs. * Demonstrates encryption and decryption. * **Critical:** In a real application, store the "key" and "iv" securely (e.g., using Vercel's Environment Variables feature with encryption, or a dedicated key management system like HashiCorp Vault). *Never* expose them in your code or logs. #### 3.3.2 HTTPS Enforcement on Vercel Vercel automatically provides and manages SSL certificates for your domains. Enforce HTTPS by: 1. **Verifying domain configuration:** Ensure your domain is correctly configured in your Vercel project and that the SSL certificate is valid. 2. **Using "strict-transport-security" header:** This header tells browsers to only access your site over HTTPS. Configure this in your "next.config.js" (or similar) file. #### 3.3.3 next.config.js Configuration """javascript // next.config.js /** @type {import('next').NextConfig} */ const nextConfig = { reactStrictMode: true, async headers() { return [ { source: '/(.*)', headers: [ { key: 'Strict-Transport-Security', value: 'max-age=31536000; includeSubDomains; preload', }, ], }, ]; }, } module.exports = nextConfig """ * **Explanation:** * The configuration above will automatically set the Strict-Transport-Security (HSTS) header for all requests, ensuring that browsers only access your site over HTTPS. ### 3.4 Anti-Patterns * **Hardcoding encryption keys:** Never hardcode encryption keys in your code. Store them securely in environment variables or a key management system. * **Using ECB mode encryption:** ECB mode is vulnerable to pattern analysis. Use CBC, GCM, or other secure modes. * **Logging sensitive data:** Redact sensitive data from logs and error messages. * **Not rotating encryption keys:** Regularly rotate encryption keys to minimize the impact of a potential key compromise. ## 4. Dependency Management ### 4.1 Standard * **Do This:** Keep your dependencies up to date with the latest security patches. Use a dependency management tool (e.g., npm, yarn, pnpm) to manage your dependencies and their versions. Use a tool like Snyk, Dependabot, or Vercel's built-in vulnerability detection to identify and remediate vulnerabilities in your dependencies. * **Don't Do This:** Use outdated dependencies with known vulnerabilities. Ignore security alerts from dependency scanning tools. Install dependencies from untrusted sources. ### 4.2 Why * **Security:** Prevents exploitation of known vulnerabilities in dependencies. * **Maintainability:** Up-to-date dependencies often include bug fixes and performance improvements. * **Compliance:** Many regulations require patching known vulnerabilities. ### 4.3 Code Examples #### 4.3.1 Using Dependabot on GitHub (Recommended) 1. **Enable Dependabot:** If your Vercel project is linked to a GitHub repository, enable Dependabot version updates and security updates in your repository settings (Security -> Dependabot). 2. **Review and merge pull requests:** Dependabot will automatically create pull requests to update your dependencies. Review these pull requests carefully and merge them to keep your dependencies up to date. #### 4.3.2 Using Snyk CLI locally 1. **Install Snyk:** """bash npm install -g snyk """ 2. **Authenticate Snyk:** """bash snyk auth """ 3. **Test your project:** """bash snyk test """ This command scans your project for vulnerabilities and provides remediation advice #### 4.3.3 Using npm audit """bash npm audit """ This command will scan your package-lock.json or package.json file for known vulnerabilities. * Update vulnerable packages by : """bash npm audit fix """ ### 4.4 Anti-Patterns * **Ignoring Dependabot alerts:** Treat Dependabot alerts seriously and address them promptly. * **Disabling dependency scanning:** Never disable dependency scanning tools. * **Installing dependencies globally:** Avoid installing dependencies globally, as this can lead to conflicts and inconsistencies. * **Using wildcard version ranges:** Avoid using overly broad version ranges (e.g., "^1.0.0") in your "package.json" file, as this can introduce breaking changes unexpectedly. Use more specific version ranges (e.g., "~1.0.0" or "1.0.0"). * **Committing "node_modules":** Under no circumstance commit the "node_modules" directory to version control. ## 5. Error Handling and Logging ### 5.1 Standard * **Do This:** Implement proper error handling and logging to capture and analyze errors. Log sufficient information to diagnose and resolve issues, but avoid logging sensitive data. Implement centralized logging to enable efficient analysis. * **Don't Do This:** Expose detailed error messages to users. Log sensitive data. Ignore errors. ### 5.2 Why * **Security:** Helps identify and respond to security incidents. Prevents attackers from gathering information about your system through error messages. * **Maintainability:** Enables quick diagnosis and resolution of issues. * **Performance:** Helps identify performance bottlenecks. ### 5.3 Code Examples #### 5.3.1 Centralized Logging with Vercel Analytics Vercel Analytics provides built-in error tracking and logging capabilities. Use it to: 1. **Track errors in production:** Vercel Analytics automatically captures errors that occur in your application. 2. **Analyze error trends:** Use the Vercel Analytics dashboard to identify common error patterns and prioritize remediation efforts. 3. **Correlate errors with deploys:** Vercel Analytics allows you to correlate errors with specific deployments, making it easier to identify the root cause of issues. #### 5.3.2 Custom Error Logging (Next.js API Route) """typescript // pages/api/example.ts import { NextApiRequest, NextApiResponse } from 'next'; export default async function handler(req: NextApiRequest, res: NextApiResponse) { try { // Your code here if (Math.random() < 0.5) { throw new Error('Simulated error'); } res.status(200).json({ message: 'Success' }); } catch (error: any) { console.error('API Error:', error); // Log the error to console (for Vercel logs) // In a production environment, you'd send this error to a central logging service // Example: sentry.captureException(error); res.status(500).json({ error: 'Internal server error' }); // Generic error message } } """ * **Explanation:** * The code includes a "try...catch" block to handle errors gracefully. * It logs the error to the console using "console.error()". Vercel automatically captures these console logs. * It sends a generic error message to the client to avoid exposing sensitive information. * **Important:** In a production environment, integrate with a dedicated logging service like Sentry, Datadog, or similar. #### 5.3.4 Data masking functions for protecting sensitive information """typescript function maskEmail(email: string): string { const [username, domain] = email.split('@'); const maskedUsername = username.slice(0, 2) + '*'.repeat(username.length - 2); return "${maskedUsername}@${domain}"; } function maskPhoneNumber(phoneNumber: string): string { return phoneNumber.replace(/(\d{3})\d{3}(\d{4})/, '$1***$2'); } """ * **Explanation** * The "maskEmail" function keeps the first two characters of the username and replaces the rest with asterisks. * The "maskPhoneNumber" function keeps the first three and last four digits, replacing the middle digits with asterisks. ### 5.4 Anti-Patterns * **Exposing stack traces to users:** Never expose stack traces or other detailed error information to users. This can reveal sensitive information about your system. * **Logging passwords or API keys:** Never log sensitive data like passwords or API keys. * **Ignoring unhandled exceptions:** Always handle unhandled exceptions to prevent your application from crashing. Implement global error handlers to catch unexpected errors. * **Over-logging:** Avoid logging excessively, as this can impact performance and storage costs. ## 6. Environment Variables and Secrets Management ### 6.1 Standard * **Do This:** Store sensitive information (e.g., API keys, database passwords) in environment variables. Encrypt environment variables if required. Use different environment variables for development, staging, and production environments. Use Vercel's built in secrets management. * **Don't Do This:** Hardcode secrets in your code. Commit secrets to version control. Use the same secrets for all environments. ### 6.2 Why * **Security:** Protects sensitive data from unauthorized access. * **Maintainability:** Simplifies configuration management. * **Compliance:** Many regulations require protecting secrets. ### 6.3 Code Examples #### 6.3.1 Using Vercel Environment Variables 1. **Set environment variables in the Vercel dashboard:** Go to your Vercel project settings and add environment variables under the "Environment Variables" section. 2. **Access environment variables in your code:** Use "process.env.VARIABLE_NAME" to access environment variables in your code. """typescript // pages/api/data.ts import { NextApiRequest, NextApiResponse } from 'next'; export default async function handler(req: NextApiRequest, res: NextApiResponse) { const apiKey = process.env.API_KEY; if (!apiKey) { console.error('API_KEY environment variable is not set.'); return res.status(500).json({ error: 'Internal server error' }); } // Use the API key to fetch data // ... } """ #### 6.3.2 Encrypting Environment Variables Vercel offers the ability to encrypt environment variables within the Vercel dashboard for enhanced security. ### 6.4 Anti-Patterns * **Committing ".env" files:** Never commit ".env" files (containing local environment variables) to version control. Add ".env" to your ".gitignore" file. Vercel's environment variables are set via the dashboard and do not use ".env" files in production. * **Using default secrets:** Avoid using default secrets provided by third-party services. Change them to strong, randomly generated values. * **Exposing secrets in client-side code:** Never expose secrets in client-side JavaScript code. Use API routes or serverless functions to access secrets securely. ## 7. Denial-of-Service (DoS) Protection ### 7.1 Standard *Implement rate limiting to protect your application from being overwhelmed by excessive requests. *Implement input validation and sanitization to prevent attackers from injecting malicious data that could consume excessive resources. *Utilize Vercel's built-in caching mechanisms and CDN to reduce the load on your origin servers. *Implement connection limits to limit the number of concurrent connections from a single IP address or user.* ### 7.2 Why * Prevents attackers from overwhelming the application with malicious requests. Reduces service unavailabilty. Protects from large scale attacks. ### 7.3 Code Examples #### 7.3.1 Rate Limiting Implementation * This can be implemented in Node.js environment using libraries like "express-rate-limit" or "limiter". Rate Limiting can also be implemented at a reverse proxy level. """typescript // middleware.ts import { NextResponse } from 'next/server' import { RateLimiter } from 'limiter' import type { NextRequest } from 'next/server' const limiter = new RateLimiter({tokensPerInterval: 50, interval: 'minute'}) export async function middleware(req: NextRequest) { const remaining = await limiter.removeTokens(1) if (remaining < 0) { return new NextResponse( JSON.stringify({ message: 'Too Many Requests' }), { status: 429, headers: { 'content-type': 'application/json', }, } ) } return NextResponse.next(); } // See "Matching Paths" below to learn more export const config = { matcher: ['/api/:path*'] } """ * **Explanation:** * This uses Next.js middleware. * "tokensPerInterval" is the amount of calls allowed per "interval". In this case 50 per minute. ### 7.4 Anti-Patterns * Not implementing DDoS protection. * Not using a DDoS prevention service. * Ignoring suspicious traffic patterns. ## 8. Security Audits and Penetration Testing ### 8.1 Standard * Conduct regular security audits of your code and infrastructure to identify potential vulnerabilities. * Perform penetration testing to simulate real-world attacks and identify weaknesses in your application. * Engage external security experts to provide independent assessments and recommendations. * Establish a process for addressing and remediating identified vulnerabilities in a timely manner. ### 8.2 Why * Proactively identifies and address security vulnerabilities. Provides a high-level of code and application security * Helps to prevent security incidents and data breaches. ### 8.3 Code Examples * Tools such as OWASP ZAP or Burp Suite can perform automated testing of web application security. Regular code reviews utilizing the above security standards are also considered part of auditing. ### 8.4 Anti-Patterns * No use of Security Audits. * No code reviews. * Not using penetration testing. By implementing these security best practices, you can significantly reduce your application's risk profile and protect your data and users. Regularly review and update these standards to keep pace with the evolving threat landscape.