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