# 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 (
Name:
setName(e.target.value)}
/>
{errors.name && <p>{errors.name}</p>}
Email:
setEmail(e.target.value)}
/>
{errors.email && <p>{errors.email}</p>}
Message:
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.
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.
# Tooling and Ecosystem Standards for Vercel This document outlines coding standards and best practices related to tooling and the Vercel ecosystem. Adhering to these guidelines ensures maintainable, performant, and secure applications deployed on Vercel. These standards are designed to be used directly by developers and as context by AI coding assistants to generate and validate high-quality Vercel applications. ## 1. Development Environment and Tooling ### 1.1. Recommended IDE/Editor * **Do This:** Use VS Code with the official Vercel extension, ESLint, Prettier, and TypeScript extensions. * **Don't Do This:** Rely on basic text editors without linting, formatting, and type-checking capabilities. **Why:** VS Code with appropriate extensions provides excellent support for Vercel development, including syntax highlighting, code completion, linting, formatting, and integration with Vercel CLI and platform features. This reduces development time and improves code quality. **Example (VS Code settings.json):** """json { "editor.formatOnSave": true, "editor.defaultFormatter": "esbenp.prettier-vscode", "eslint.enable": true, "eslint.validate": [ "javascript", "javascriptreact", "typescript", "typescriptreact" ], "typescript.tsdk": "node_modules/typescript/lib" } """ ### 1.2. Vercel CLI * **Do This:** Use the latest version of the Vercel CLI for local development, deployments, and managing Vercel projects from the command line. * **Don't Do This:** Rely solely on the Vercel dashboard without leveraging the CLI for automation and scripting. **Why:** The Vercel CLI provides essential tools for managing deployments, environment variables, secrets, domains, and other aspects of your Vercel project. It enables automation through scripts and CI/CD workflows, streamlining the development and deployment process. **Example (Deploying with Vercel CLI):** """bash vercel deploy --prod """ This command deploys the current project to the production environment on Vercel. ### 1.3. Version Control (Git) * **Do This:** Use Git for version control and follow a branching strategy (e.g., Gitflow or GitHub Flow). * **Don't Do This:** Skip version control or directly commit to the main branch without code review. **Why:** Git enables collaboration, tracks changes, and allows you to revert to previous versions. Branching strategies ensure code isolation, facilitate feature development, and simplify the release process. **Example (GitHub Workflow):** 1. Create a new branch for each feature or bug fix. 2. Make commits with clear and descriptive messages. 3. Create a pull request to merge the branch into the main branch. 4. Conduct code review. 5. Merge the pull request. ### 1.4. Package Manager (npm, yarn, pnpm) * **Do This:** Use npm, yarn, or pnpm for managing project dependencies. Consider using pnpm for optimized disk space and faster installations due to its symlinking approach. Use "package-lock.json" (npm), "yarn.lock" (yarn), or "pnpm-lock.yaml" (pnpm) to ensure deterministic builds. * **Don't Do This:** Manually manage dependencies or skip lockfiles. **Why:** Package managers automate dependency installation, versioning, and updating. Lockfiles guarantee consistent builds across different environments by pinning down the exact versions of dependencies. **Example (pnpm install):** """bash pnpm install """ ### 1.5. Environment Variables * **Do This:** Use Vercel's environment variables feature to manage configuration settings. Store sensitive information (API keys, database passwords) as encrypted environment variables within the Vercel project settings. Use ".env.local" for local development (ignored on production through ".vercelignore" or ".gitignore"). * **Don't Do This:** Hardcode configuration settings in your code or expose sensitive information in client-side JavaScript. **Why:** Environment variables provide a secure and flexible way to configure your application without modifying the code. Vercel's environment variables feature allows you to manage different configurations for different environments (development, staging, production). **Example (Accessing environment variables in Next.js):** """javascript // pages/api/hello.js export default function handler(req, res) { const apiKey = process.env.API_KEY; // ... use apiKey res.status(200).json({ name: 'John Doe' }) } """ **Example (.vercelignore):** """ .env.local """ ### 1.6 Linting and Formatting * **Do This:** Use ESLint and Prettier to enforce consistent code style and identify potential errors. Configure ESLint with recommended rulesets (e.g., "eslint:recommended", "plugin:@typescript-eslint/recommended", "next/core-web-vitals"). Integrate Prettier with ESLint to automatically format code on save. * **Don't Do This:** Ignore linting errors or rely on inconsistent code formatting. **Why:** Linting and formatting tools improve code readability, maintainability, and prevent common coding errors. Consistency in code style is crucial for team collaboration. **Example (.eslintrc.json):** """json { "extends": [ "eslint:recommended", "plugin:@typescript-eslint/recommended", "next/core-web-vitals", "prettier" ], "plugins": ["@typescript-eslint", "prettier"], "rules": { "prettier/prettier": "error", "no-unused-vars": "warn", "@typescript-eslint/no-unused-vars": "warn" } } """ **Example (.prettierrc.json):** """json { "semi": false, "singleQuote": true, "trailingComma": "all", "printWidth": 120 } """ ### 1.7 Testing * **Do This:** Implement unit, integration, and end-to-end tests using tools like Jest, React Testing Library, Cypress, or Playwright. Automate tests as part of your CI/CD pipeline. * **Don't Do This:** Skip testing or rely solely on manual testing. **Why:** Automated testing ensures code quality, prevents regressions, and facilitates continuous integration and delivery. Different types of tests cover different aspects of your application, from individual components to the entire user experience. **Example (Jest unit test):** """javascript // sum.test.js import { sum } from './sum'; test('adds 1 + 2 to equal 3', () => { expect(sum(1, 2)).toBe(3); }); """ **Example (Cypress end-to-end test):** """javascript // cypress/e2e/spec.cy.js describe('My First Test', () => { it('Visits the Kitchen Sink', () => { cy.visit('https://example.cypress.io') cy.contains('type').click() cy.url().should('include', '/commands/actions') cy.get('.action-email') .type('fake@email.com') .should('have.value', 'fake@email.com') }) }) """ ## 2. Vercel Integrations and Features ### 2.1. Vercel Analytics * **Do This:** Utilize Vercel Analytics to monitor application performance, identify bottlenecks, and improve user experience. Pay attention to metrics like Core Web Vitals (LCP, FID, CLS) and adjust the application for optimal scores. * **Don't Do This:** Ignore analytics data or rely on external analytics tools without leveraging Vercel's built-in capabilities. **Why:** Vercel Analytics provides deep insights into your application's performance and user behavior. By understanding these metrics, you can optimize your code, infrastructure, and content delivery for better performance and engagement. **Example:** Setting up Vercel Analytics is usually automatic upon deployment if you have a supported framework, you may still need to enable it in the Vercel project settings. ### 2.2. Vercel Functions * **Do This:** Use Vercel Functions for serverless code execution, such as API endpoints, background tasks, and data processing. Write concise and efficient functions to minimize cold starts. Implement error handling and logging. Use middleware for common tasks like authentication and authorization. * **Don't Do This:** Overload functions with complex logic or use them for long-running tasks. **Why:** Vercel Functions allow you to run server-side code without managing servers. They are ideal for handling dynamic requests, processing data, and integrating with third-party services. Keeping functions lightweight improves performance and reduces costs. **Example (Vercel Function - API endpoint):** """javascript // api/hello.js export default async function handler(req, res) { try { const data = { message: 'Hello from Vercel Functions!' }; console.log("Function Executed Successfully"); // Logging res.status(200).json(data); } catch (error) { console.error("Function Failed", error); res.status(500).json({ error: 'Internal Server Error' }); } } """ ### 2.3. Edge Functions (Middleware) * **Do This:** Utilize Edge Functions for tasks that require low latency and global distribution, such as A/B testing, authentication, and personalization. Ensure Edge Functions are lightweight and performant. Configure "middleware.ts" or "middleware.js" at the root of the project. * **Don't Do This:** Use Edge Functions for complex computations or data processing tasks. **Why:** Edge Functions run closer to the user, reducing latency and improving the user experience. They are suitable for tasks that need to be executed quickly and globally. **Example (Next.js Middleware for Authentication):** """typescript // middleware.ts import { NextResponse } from 'next/server' import type { NextRequest } from 'next/server' // This function can be marked "async" if using "await" inside export function middleware(request: NextRequest) { if (request.nextUrl.pathname.startsWith('/admin')) { return NextResponse.rewrite(new URL('/login', request.url)) } } // See "Matching Paths" below to learn more export const config = { matcher: '/admin/:path*', } """ ### 2.4. Vercel KV (Redis) * **Do This:** Consider using Vercel KV for low-latency data caching, session management, rate limiting, and other real-time applications. Use it to cache data close to the edge. * **Don't Do This:** Use it as a primary database. **Why:** Vercel KV provices a flexible, globally-distributed key-value store that's optimized for low-latency reads. ### 2.5. Vercel Blob * **Do This:** Utilize Vercel Blob to store and serve static assets like images, videos, and documents. Optimize assets for performance (e.g., compress images, use appropriate formats). * **Don't Do This:** Store large files in your Git repository or serve assets directly from your application code. **Why:** Vercel Blob provides a scalable and cost-effective way to store and serve static assets. It simplifies asset management and improves application performance by offloading asset delivery to a dedicated service. ### 2.6 Vercel Postgres * **Do This:** Use Vercel Postgres when you need a full relational database in production integrated directly into the Vercel platform. * **Don't Do This:** Use it without proper connection pooling, or without understanding limits of the Hobby tier (if applicable). ### 2.7 Turborepo * **Do This**: Use Turborepo in monorepos to optimize build and deployment times by caching and parallelizing tasks. Configure "turbo.json" to define task dependencies and caching behavior. * **Don't Do This**: Manually manage dependencies and build processes in monorepos. **Why:** Turborepo dramatically improve the developer experience in monorepos by optimizing build and deployment processes. **Example (turbo.json):** """json { "$schema": "https://turbo.build/schema.json", "pipeline": { "build": { "dependsOn": ["^build"], "outputs": [".next/**", "!.next/cache/**"] }, "test": {}, "lint": {} } } """ ### 2.8 "vercel.json" Configuration * **Do This:** Configure your "vercel.json" file to optimize your deployment settings, including routes, headers, and redirects. Use the "rewrites" and "redirects" sections for SEO-friendly URLs. Customize headers for caching and security (e.g., "Cache-Control", "Content-Security-Policy"). * **Don't Do This:** Leave the "vercel.json" file with default settings or use it for complex application logic. **Why:** The "vercel.json" file provides fine-grained control over your Vercel deployment, enabling you to customize the behavior of your application and optimize it for performance and security. **Example (vercel.json):** """json { "rewrites": [ { "source": "/blog/:slug", "destination": "/blog/[slug]" } ], "headers": [ { "source": "/(.*)", "headers": [ { "key": "Cache-Control", "value": "public, max-age=31536000, immutable" } ] } ], "redirects": [ { "source": "/old-page", "destination": "/new-page", "permanent": true } ] } """ ### 2.9 Using AI Copilots (GitHub Copilot, Cursor, etc.) * **Do This:** Configure your AI copilot tools (Github Copilot, Cursor, etc.) to use the project's ".eslintrc.json", ".prettierrc.json" and "tsconfig.json" files to ensure code generation and completion adhere to the team's coding standards and TypeScript configuration. Explicitly provide context to your AI copilot about Vercel-specific features (e.g., "Write a Vercel Function that...") to guide its code generation. Use AI copilot for generating boilerplate code, writing tests, and refactoring existing code, but always review the generated code carefully to ensure it meets the project's requirements. * **Don't Do This:** Blindly accept AI-generated code without review or understanding. Use AI copilots without proper configuration, which leads to code that does not match the project's coding style. **Why:** AI copilots can significantly increase developer productivity by automating repetitive tasks and suggesting code completions. Configuring these tools with project-specific standards ensures that generated code is consistent with the existing codebase and follows best practices. However, a human review is always necessary to ensure code quality and prevent introducing errors. **Example (Prompting Copilot):** Bad: "Write an API endpoint" Good: "Write a Vercel Function that creates an API endpoint using Next.js API routes, connects to Vercel Postgres, and returns a JSON response with a success message." ### 2.10 Continuous Integration and Continuous Deployment (CI/CD) * **Do This:** Integrate your Vercel project with a CI/CD system (e.g., GitHub Actions, GitLab CI, CircleCI) to automate builds, tests, and deployments. Configure automated deployments for pull requests and merges to the main branch. Use environments (staging, production) to manage different deployment configurations. * **Don't Do This:** Manually deploy code or skip automated testing in your CI/CD pipeline. **Why:** CI/CD automates the software delivery process, ensuring consistent and reliable deployments. It reduces the risk of human error and speeds up the release cycle. **Example (GitHub Actions workflow):** """yaml # .github/workflows/deploy.yml name: Deploy to Vercel on: push: branches: - main jobs: deploy: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - name: Install Vercel CLI run: npm install --global vercel - name: Deploy to Vercel run: vercel deploy --prod --token ${ secrets.VERCEL_TOKEN } """ This document provides a comprehensive set of coding standards and best practices for Vercel development related to Tooling and Ecosystem. By following these guidelines, development teams can build high-quality, performant, and secure applications on the Vercel platform. Remember to stay updated with the latest Vercel features and best practices to remain at the forefront of modern web development.