# API Integration Standards for Bun
This document outlines coding standards for API integration within Bun projects. It aims to ensure maintainability, performance, security, and consistency across all aspects of interacting with external services and backend systems. These standards are tailored for use with the latest Bun features and best practices.
## 1. Architectural Considerations
### 1.1 Abstraction and Separation of Concerns
* **Do This:** Abstract API interaction logic into dedicated modules or services. Shield core application logic from the specifics of API endpoints, request structures, and response formats.
* **Don't Do This:** Embed API calls directly within UI components or business logic without abstraction. This creates tight coupling and makes your code harder to test and refactor.
* **Why:** Abstraction improves testability, maintainability, and allows for easier swapping of API implementations (e.g., switching from one payment gateway to another or mocking APIs for testing). It reduces the impact of API changes on the rest of the application.
* **Example:**
"""typescript
// api-client.ts (Abstraction Layer)
import { request } from 'undici'; // Undici, Bun's built-in HTTP client
interface User {
id: number;
name: string;
email: string;
}
const API_URL = process.env.API_URL || 'https://example.com/api';
async function fetchUser(id: number): Promise {
try {
const response = await request("${API_URL}/users/${id}");
if (response.statusCode !== 200) {
throw new Error("HTTP error! status: ${response.statusCode}");
}
const { body } = await response.body.json();
return body as User;
} catch (error) {
console.error("Error fetching user:", error);
throw error; // Re-throw for handling higher up
}
}
export { fetchUser };
// user-service.ts (Usage)
import { fetchUser } from './api-client';
async function getUserProfile(userId: number): Promise {
try {
const user = await fetchUser(userId);
// Additional business logic specific to user retrieval.
return user;
} catch (error) {
console.error("Failed to get user profile:", error);
throw new Error("Unable to retrieve user profile.");
}
}
export { getUserProfile };
"""
### 1.2 Environment Configuration
* **Do This:** Externalize API endpoints, authentication tokens, and other configuration parameters using environment variables. Use ".env" files during development and environment-specific configuration in production. In Bun, leverage the built-in ".env" file support.
* **Don't Do This:** Hardcode API URLs or sensitive information directly into your codebase.
* **Why:** This simplifies deployments and reduces the risk of accidentally exposing sensitive information in version control. It allows for different API configurations across different environments (development, staging, production).
* **Example:**
Create a ".env" file:
"""
API_URL=https://api.example.com
API_KEY=your_secret_api_key
"""
Access the config through "process.env":
"""typescript
const apiUrl = process.env.API_URL;
const apiKey = process.env.API_KEY;
if (!apiUrl || !apiKey) {
console.error("API_URL or API_KEY not configured!");
}
"""
### 1.3 Error Handling and Resilience
* **Do This:** Implement robust error handling, including retry mechanisms (with exponential backoff), circuit breaking, and proper logging. Utilize "try...catch" blocks and handle specific error types to provide meaningful error messages.
* **Don't Do This:** Ignore errors, only log generic error messages, or assume API calls will always succeed.
* **Why:** Handling API errors gracefully makes your application more resilient to transient network issues or API outages. Provides a better user experience by avoiding crashes or unclear error states.
* **Example:**
"""typescript
import { request } from 'undici';
async function fetchDataWithRetry(url: string, maxRetries = 3, retryDelay = 1000) {
let retries = 0;
while (retries < maxRetries) {
try {
const response = await request(url);
if (response.statusCode >= 200 && response.statusCode < 300) {
const data = await response.body.json();
return data;
} else {
// Retry on server errors (5xx) or rate limits (429)
if (response.statusCode >= 500 || response.statusCode === 429) {
retries++;
console.warn("Attempt ${retries} failed with status ${response.statusCode}. Retrying in ${retryDelay}ms");
await new Promise(resolve => setTimeout(resolve, retryDelay));
retryDelay *= 2; // Exponential backoff
} else {
// Don't retry on client errors (4xx, except 429)
throw new Error("Request failed with status ${response.statusCode}");
}
}
} catch (error) {
console.error("Error fetching data: ${error}");
retries++;
if (retries >= maxRetries) {
throw new Error("Max retries reached. Failed to fetch data from ${url}. Last error: ${error}");
}
console.warn("Retrying in ${retryDelay}ms...");
await new Promise(resolve => setTimeout(resolve, retryDelay));
retryDelay *= 2; // Exponential backoff
}
}
throw new Error("Max retries reached. Failed to fetch data from ${url}.");
}
// Usage
async function callAPI() {
try {
const data = await fetchDataWithRetry('https://api.example.com/data');
console.log('Data:', data);
} catch (error) {
console.error('Failed to fetch data after multiple retries:', error);
// Handle the error appropriately, e.g., display an error message to the user.
}
}
"""
### 1.4 API Versioning
* **Do This:** Implement API versioning (e.g., using URL path "/api/v1/", headers, or content negotiation). Clearly document the implemented versioning strategy.
* **Don't Do This:** Make breaking changes to APIs without introducing a new version.
* **Why:** Allows for evolving APIs without breaking existing clients. Provides a smooth migration path for consumers of the API.
### 1.5 Rate Limiting
* **Do This:** Implement rate limiting on API requests to protect backend resources. Utilize middleware or dedicated libraries to manage rate limits. Consider both client-side and server-side rate limiting as appropriate (though generally this is server-side).
* **Don't Do This:** Overwhelm external APIs with excessive requests. Fail to handle "429 Too Many Requests" errors appropriately.
* **Why:** Prevents abuse, protects against denial-of-service attacks, and ensures fair usage of API resources. Improves the reliability of your application by avoiding being blocked due to exceeding rate limits.
## 2. Implementation Details
### 2.1 Choosing an HTTP Client
* **Do This:** Utilize "undici", Bun’s built-in HTTP client, for most API interactions. For more complex use cases (e.g., interceptors, automatic retries), consider libraries like "node-fetch" (with caution, see below), "axios" (with polyfills if server-side only).
* **Don't Do This:** Rely on outdated or less performant HTTP clients without a strong justification.
* **Why:** "undici" is highly performant and natively supported by Bun, offering significant performance advantages. However, it can be lower-level, needing more manual configuration for many common scenarios. "node-fetch" introduces a "fetch" compatible API, but may require polyfills when targeting only the server side.
* **Considerations for node-fetch/axios:** If you decide to use "node-fetch" or "axios" in Bun, pay close attention to polyfills. Since Bun aims to be a drop-in replacement for Node.js, some libraries may not work out-of-the-box, requiring you to install and configure polyfills for missing browser APIs. Only add them if absolutely necessary.
* **Example (Using "undici"):**
"""typescript
import { request } from 'undici';
interface Post {
userId: number;
id: number;
title: string;
body: string;
}
async function getPosts(): Promise {
try {
const response = await request('https://jsonplaceholder.typicode.com/posts');
if (response.statusCode !== 200) {
throw new Error("HTTP error! Status: ${response.statusCode}");
}
const posts = await response.body.json() as Post[];
return posts;
} catch (error) {
console.error("Error fetching posts:", error);
throw error;
}
}
getPosts()
.then(posts => console.log(posts))
.catch(error => console.error("Failed to get posts:", error));
"""
### 2.2 Data Serialization and Deserialization
* **Do This:** Use "JSON.stringify" and "JSON.parse" for JSON data. For more complex data formats (e.g., XML, Protobuf), use appropriate libraries. Implement validation to ensure data integrity. Consider using TypeScript interfaces or libraries like "zod" or "yup" to define and validate data structures.
* **Don't Do This:** Manually construct or parse JSON strings. Trust API data implicitly without validation.
* **Why:** Proper data serialization and deserialization ensure data integrity and type safety. Validation prevents unexpected errors and security vulnerabilities.
* **Example (Using "zod" for validation):**
"""typescript
import { request } from 'undici';
import { z } from "zod";
const PostSchema = z.object({
userId: z.number(),
id: z.number(),
title: z.string(),
body: z.string(),
});
type Post = z.infer; // Infer the TypeScript type
async function getPosts(): Promise {
try {
const response = await request('https://jsonplaceholder.typicode.com/posts');
if (response.statusCode !== 200) {
throw new Error("HTTP error! Status: ${response.statusCode}");
}
const rawData = await response.body.json();
// Validation using Zod
const validatedData = z.array(PostSchema).safeParse(rawData);
if (!validatedData.success){
console.error("Validation Error:", validatedData.error);
throw new Error("Invalid API response format.");
}
return validatedData.data;
} catch (error) {
console.error("Error fetching posts:", error);
throw error;
}
}
getPosts()
.then(posts => console.log(posts))
.catch(error => console.error("Failed to get posts:", error));
"""
### 2.3 Authentication and Authorization
* **Do This:** Implement secure authentication and authorization mechanisms using industry-standard protocols like OAuth 2.0, JWT, or API keys. Store sensitive credentials securely (e.g., using environment variables or a secrets management system). Use HTTPS for all API communication.
* **Don't Do This:** Hardcode credentials in code, commit secrets to version control, or use insecure protocols (HTTP).
* **Why:** Protects your APIs and your application's data from unauthorized access.
* **Example (Using API Keys):**
"""typescript
import { request } from 'undici';
const API_KEY = process.env.API_KEY;
async function fetchData(url: string) {
if (!API_KEY) {
throw new Error("API_KEY is not defined in the environment.");
}
try {
const response = await request(url, {
headers: {
'X-API-Key': API_KEY,
},
});
if (response.statusCode !== 200) {
throw new Error("HTTP error! Status: ${response.statusCode}");
}
return await response.body.json();
} catch (error) {
console.error("Error fetching data:", error);
throw error;
}
}
// Usage:
fetchData('https://api.example.com/protected/data')
.then(data => console.log(data))
.catch(error => console.error(error));
"""
### 2.4 Logging and Monitoring
* **Do This:** Log all API requests, responses, and errors with sufficient detail for debugging and monitoring. Include timestamps, request URLs, HTTP status codes, and relevant data. Use structured logging for easier analysis. Integrate with monitoring tools to track API performance and availability.
* **Don't Do This:** Log sensitive data (e.g., passwords, API keys) in plain text. Fail to monitor API performance or errors.
* **Why:** Provides insights into API usage patterns, helps to identify and resolve issues quickly, and enables proactive monitoring of API health.
* **Example:**
"""typescript
import { request } from 'undici';
async function fetchData(url: string) {
const startTime = Date.now();
try {
const response = await request(url);
const endTime = Date.now();
const duration = endTime - startTime;
console.log("API Request: ${url} - Status: ${response.statusCode} - Duration: ${duration}ms");
if (response.statusCode !== 200) {
throw new Error("HTTP error! Status: ${response.statusCode}");
}
return await response.body.json();
} catch (error) {
const endTime = Date.now();
const duration = endTime - startTime;
console.error("API Request Failed: ${url} - Duration: ${duration}ms - Error: ${error}");
throw error;
}
}
"""
## 3. Advanced Patterns
### 3.1 Caching
* **Do This:** Implement caching strategies to reduce API calls and improve performance. Use appropriate cache headers (e.g., "Cache-Control", "ETag") on the server-side and client-side. Consider using in-memory caching, Redis, or other caching solutions.
* **Don't Do This:** Cache sensitive data without proper security measures. Cache data indefinitely without invalidation strategies. Use the same Cache TTL for all requests.
* **Why:** Reduces latency, improves application responsiveness, and reduces load on backend systems.
### 3.2 GraphQL
* **Do This:** Consider using GraphQL when dealing with complex data requirements or when needing to optimize data fetching. Use libraries like "graphql-js" with "bun" or frameworks like "mercurius" (fastest GraphQL framework) or "fastify-gql".
* **Don't Do This:** Neglect the use of proper data loading techniques to prevent N+1 query problems in GraphQL resolvers.
* **Why:** GraphQL allows clients to request only the specific data they need, improving performance and reducing over-fetching. It also provides a strong type system and introspection capabilities. Bun is optimized for GraphQL.
### 3.3 Server-Sent Events (SSE) and WebSockets
* **Do This:** Use SSE or WebSockets for real-time data updates. Use appropriate libraries and protocols (e.g., "ws" for WebSockets).
* **Don't Do This:** Use polling for real-time updates when SSE or WebSockets are more appropriate.
* **Why:** Provides efficient, low-latency communication for real-time applications. Bun performance is well suited to handling numerous concurrent connections thanks to Javascript and Zig interoperability.
## 4. Bun-Specific Optimizations
### 4.1 Transpilation and Bundling
* **Do This:** Utilize Bun's built-in transpiler and bundler for optimal performance. Configure "bunfig.toml" appropriately for your project.
* **Don't Do This:** Rely on external transpilers or bundlers unless absolutely necessary.
* **Why:** Bun's native tooling is optimized for speed and efficiency within the Bun ecosystem.
### 4.2 Zig Interoperability (advanced)
* **Do This:** Consider leveraging Zig interoperability for performance-critical API integration tasks, such as parsing binary data or implementing custom protocols.
* **Don't Do This:** Use Zig without a clear understanding of its benefits and drawbacks. Keep Zig code isolated and well-tested.
* **Why:** Zig offers significant performance advantages over JavaScript for certain tasks. Bun's Zig integration provides a powerful way to optimize critical sections of your code.
### 4.3 Bun.serve
* **Do This:** Use the "Bun.serve" API for creating web servers and handling API endpoints.
* **Don't Do This:** Use other server implementations that lack Bun's performance benefits unless there is a specific reason.
* **Why:** Bun's built-in server is faster and more memory-efficient than many Node.js alternatives.
These coding standards provide a foundation for building robust, performant, and secure API integrations within Bun projects. By adhering to these guidelines, development teams can ensure code consistency, improve maintainability, and deliver high-quality applications. Remember to adapt and refine these standards based on the specific needs and circumstances of your project.
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'
# Component Design Standards for Bun This document outlines coding standards specifically for component design in Bun. It aims to provide guidance for creating reusable, maintainable, and performant components within the Bun runtime environment. These standards are designed to be used both by developers and AI coding assistants. ## 1. Component Architecture & Philosophy ### 1.1. Embrace Functional Components and Reactivity **Standard:** Favor functional components over class-based components. Leverage Bun's native support for reactivity and its compatibility with JavaScript frameworks utilizing reactivity, such as React and Preact. **Do This:** Use functional components with hooks for managing state and side effects. **Don't Do This:** Rely primarily on class-based components unless specifically justified by framework requirements or legacy codebases. **Why:** Functional components are generally easier to read, test, and maintain. They promote a more declarative style of programming. By leveraging reactivity, components can efficiently update only the necessary parts of the UI when data changes. **Example:** """javascript // Functional component using React with Bun import React, { useState } from 'react'; function Counter() { const [count, setCount] = useState(0); const increment = () => { setCount(count + 1); }; return ( <div> <p>Count: {count}</p> <button onClick={increment}>Increment</button> </div> ); } export default Counter; """ ### 1.2. Single Responsibility Principle (SRP) **Standard:** Each component should have a single, well-defined responsibility. **Do This:** Decompose complex components into smaller, more focused components. **Don't Do This:** Create "god components" that handle multiple unrelated tasks. **Why:** SRP improves code readability, maintainability, and testability. It also promotes reusability, as smaller components can be composed in different ways to create new functionality. **Example:** Instead of a single "UserProfile" component that fetches user data, displays profile information, and handles profile editing, break it down into: * "UserFetch": Fetches user data. * "UserProfileDisplay": Displays user profile information. * "UserProfileEditor": Handles profile editing. ### 1.3. Composition over Inheritance **Standard:** Prefer composition over inheritance for code reuse. **Do This:** Create components that can be composed together to create more complex functionality. **Don't Do This:** Rely heavily on inheritance hierarchies to share code between components. **Why:** Composition is more flexible and less prone to the fragile base class problem. It allows you to combine components in various ways without creating tight coupling. Inheritance creates tight coupling making refactoring more difficult. **Example:** """javascript // Composition using React function Button(props) { return ( <button className={"button ${props.className}"} onClick={props.onClick}> {props.children} </button> ); } function PrimaryButton(props) { return ( <Button className="primary-button" {...props}> {props.children} </Button> ); } // Usage <PrimaryButton onClick={() => console.log('Clicked!')}>Click Me</PrimaryButton> """ Here "PrimaryButton" *composes* "Button" instead of inheriting from it. The CSS class styles the "Button" element. ### 1.4. API Design: Props and Events **Standard:** Design clear and concise component APIs using props for configuration and events for communication. **Do This:** * Use descriptive prop names. * Define prop types with "propTypes" or TypeScript. * Pass data down via props. * Communicate events up via callbacks. * Use default prop values where appropriate. **Don't Do This:** * Rely on global state directly within components (except for specific framework-managed contexts). * Mutate props directly. * Create overly complex prop structures. **Why:** Well-defined component APIs make components easier to understand, use, and test. They also reduce the risk of unexpected behavior. Clear interfaces are critical for maintaining a modular architecture. **Example (React with Prop Types):** """javascript import React from 'react'; import PropTypes from 'prop-types'; function Greeting(props) { return <p>Hello, {props.name}!</p>; } Greeting.propTypes = { name: PropTypes.string.isRequired, }; Greeting.defaultProps = { name: 'Guest', }; export default Greeting; """ ## 2. Component Implementation Details ### 2.1. State Management **Standard:** Use appropriate state management techniques based on component complexity and scope. **Do This:** * Use local component state for simple, isolated state. * Use context/providers for sharing state between related components. * Leverage state management libraries (e.g., Redux, Zustand, Jotai) for complex, application-wide state. **Don't Do This:** * Overuse global state for local component concerns. * Mutate state directly without using "setState" or appropriate state management hooks. **Why:** Proper state management ensures predictable component behavior and prevents performance issues. **Example (React with useState hook):** """javascript import React, { useState } from 'react'; function Toggle() { const [isOn, setIsOn] = useState(false); const toggle = () => { setIsOn(!isOn); }; return ( <button onClick={toggle}> {isOn ? 'On' : 'Off'} </button> ); } export default Toggle; """ ### 2.2. Rendering Optimization **Standard:** Optimize component rendering for performance. **Do This:** * Use "React.memo" or similar techniques to prevent unnecessary re-renders. * Implement "shouldComponentUpdate" (if using class components) or "useMemo" to control render updates. * Use virtualization techniques for large lists. * Debounce or throttle event handlers for performance-sensitive updates. **Don't Do This:** * Force re-renders unnecessarily. * Perform heavy calculations or expensive operations during rendering. **Why:** Optimizing rendering prevents performance bottlenecks, especially in complex UIs. **Example (React with React.memo):** """javascript import React from 'react'; function DisplayName({ name }) { console.log("DisplayName re-rendered!"); // For debugging return <p>Name: {name}</p>; } export default React.memo(DisplayName, (prevProps, nextProps) => { // Return true if props are equal, preventing re-render. return prevProps.name === nextProps.name; }); //Usage function App() { const [name, setName] = React.useState("Initial Name"); return ( <div> <DisplayName name={name} /> <button onClick={() => setName("New Name")}>Change Name</button> </div> ); } """ In this example, the "DisplayName" component will only re-render if the "name" prop changes. ### 2.3. Asynchronous Operations **Standard:** Handle asynchronous operations within components correctly. **Do This:** * Use "async/await" syntax for cleaner asynchronous code. * Handle errors appropriately with "try/catch" blocks or ".catch()" methods. * Use "useEffect" hook in React for performing side effects (including asynchronous operations) after rendering. **Don't Do This:** * Ignore errors from asynchronous operations. * Update component state after the component has been unmounted (leading to potential memory leaks). Use "AbortController" to manage asynchronous operations and cancel them when the component unmounts. **Why:** Proper asynchronous handling ensures that components behave predictably and avoids common errors like race conditions and memory leaks. **Example (React with useEffect and async/await):** """javascript import React, { useState, useEffect } from 'react'; function DataFetcher({ url }) { const [data, setData] = useState(null); const [isLoading, setIsLoading] = useState(true); const [error, setError] = useState(null); useEffect(() => { const fetchData = async () => { setIsLoading(true); try { const response = await fetch(url); if (!response.ok) { throw new Error("HTTP error! status: ${response.status}"); } const json = await response.json(); setData(json); } catch (e) { setError(e); } finally { setIsLoading(false); } }; fetchData(); }, [url]); // useEffect depends on the 'url' prop if (isLoading) { return <p>Loading...</p>; } if (error) { return <p>Error: {error.message}</p>; } return ( <div> <pre>{JSON.stringify(data, null, 2)}</pre> </div> ); } export default DataFetcher; """ ### 2.4. Accessibility (a11y) **Standard:** Build accessible components that are usable by people with disabilities. **Do This:** * Use semantic HTML elements. * Provide alternative text for images. * Ensure proper keyboard navigation. * Use ARIA attributes to enhance accessibility. * Test components with accessibility tools (e.g., Axe). **Don't Do This:** * Rely solely on visual cues. * Use generic elements (e.g., "<div>", "<span>") without appropriate semantic roles. **Why:** Accessibility makes your application usable by a wider audience and is often a legal requirement. **Example (Accessible Button):** """javascript import React from 'react'; function AccessibleButton({ onClick, children, ariaLabel }) { return ( <button onClick={onClick} aria-label={ariaLabel}> {children} </button> ); } export default AccessibleButton; """ ### 2.5. Testing **Standard:** Write unit tests, integration tests, and end-to-end tests for your components. **Do This:** * Use testing frameworks like Jest or Vitest. * Test component behavior in various scenarios. * Use mocking to isolate components during testing. * Strive for high test coverage. * Write tests for edge cases. **Don't Do This:** * Skip testing complex components. * Write tests that are too tightly coupled to implementation details. * Rely solely on manual testing. **Why:** Testing ensures that your components function correctly and reduces the risk of regressions. Automated tests are crucial for maintainability and continuous integration. Bun is compatible with all Javascript testing frameworks and runners. **Example (Jest test for Counter Component):** """javascript import React from 'react'; import { render, screen, fireEvent } from '@testing-library/react'; import Counter from './Counter'; test('increments count when button is clicked', () => { render(<Counter />); const incrementButton = screen.getByText('Increment'); fireEvent.click(incrementButton); const countElement = screen.getByText('Count: 1'); expect(countElement).toBeInTheDocument(); }); """ ## 3. Bun-Specific Considerations ### 3.1. Performance Benefits **Standard:** Leverage Bun's performance advantages when designing components. **Do This:** * Use Bun's fast file system APIs for component data loading scenarios (where applicable and appropriate for design context). * Benchmark different component implementations to identify performance bottlenecks. * Use Bun's faster startup time to your advantage when rendering server-side components. **Don't Do This:** * Assume that Bun automatically optimizes all component code; profiling and optimization are still necessary. * Neglect standard web performance optimizations (e.g., code splitting, lazy loading). **Why:** Bun's performance improvements can significantly improve the responsiveness and user experience of your components. However, you still need to write efficient code and apply standard best practices. ### 3.2. Ecosystem Compatibility **Standard:** Ensure compatibility with the broader JavaScript ecosystem. **Do This:** * Use standard ES modules. * Follow established npm/yarn package management practices. * Leverage existing React, Preact, or other framework component libraries (if applicable). **Don't Do This:** * Rely on non-standard Bun-specific features unless absolutely necessary. * Create custom component ecosystems that are incompatible with existing tools and libraries. **Why:** Maintaining ecosystem compatibility ensures that your components can be easily reused and integrated with other projects. ### 3.3. Server Components in Bun **Standard:** Utilize server components wisely, especially when using React Server Components. **Do This:** * Fetch data directly on the server for improved security and performance. * Keep server components close to the data source and separate from client-side interactivity. * Take advantages of the server's file system and database access capabilities. **Don't Do This:** * Introduce client-side dependencies in server components (they don't execute on the client). * Handle UI interactivity and state management on the server. They should be in client components. **Example:** """javascript // Server Component (fetching data) import { Database } from 'bun:sqlite' async function getProducts() { const db = new Database('mydb.sqlite'); const products = db.query("SELECT * FROM products").all(); db.close(); return products } export default async function ProductList() { const products = await getProducts(); return ( <ul> {products.map((product) => ( <li key={product.id}>{product.name}</li> ))} </ul> ); } """ ### 3.4. Bun Plugins for Components **Standard:** Create and use Bun plugins to extend component functionality. **Do This:** * Use Bun plugins to transform component code during the build process. * Create plugins for tasks such as CSS-in-JS compilation, image optimization, and code minification. * Publish your component plugins to the Bun package registry. **Don't Do This:** * Overuse plugins for simple component transformations. * Create plugins that modify component behavior at runtime. **Example:** """javascript // bun-plugin-example.js export default { name: "example-plugin", setup(build) { build.onLoad({ filter: /\.css$/ }, async (args) => { // Transform CSS files const contents = await Bun.file(args.path).text(); const transformedContents = transformCSS(contents); // Implement your CSS transformation logic return { contents: transformedContents, loader: "css" }; }); }, }; // bunfig.toml [plugins] "example-plugin" = "./bun-plugin-example.js" """ ## 4. Common Anti-Patterns * **God Components:** Components responsible for too many unrelated tasks. * **Prop Drilling:** Passing props through multiple layers of components that don't need them. Use context or state management libraries to avoid this. * **Tight Coupling:** Components that are highly dependent on each other, making it difficult to reuse or modify them independently. * **Ignoring Error Handling:** Failing to handle errors in asynchronous operations or other potentially failing code. * **Over-optimization:** Spending too much time optimizing components that are not performance bottlenecks. Measure before optimizing. * **Lack of Testing:** Failing to write tests for components, leading to regressions and difficult maintenance. * **Direct DOM Manipulation:** Directly manipulating the DOM without using the framework's facilities unless there is a very specific and compelling reason to do so. Increases complexity and risk. * **Global State Abuse:** Using global state for localized component concerns. This creates unnecessary dependencies. ## 5. Conclusion These component design standards for Bun provide a foundation for building high-quality, maintainable, and performant applications. By following these guidelines, developers can create components that are easy to understand, reuse, test, and extend. Remember that the best standards are those which are adopted and followed by the team. So, ensure the standards are clear, concise and readily available when needed. Regularly review this document and iterate on it to adapt to the ever-evolving landscape of web development.
# Security Best Practices Standards for Bun This document outlines the security best practices for Bun development. Following these standards will help create secure, maintainable, and performant Bun applications. ## 1. Input Validation and Sanitization ### 1.1 Standard: Validate and Sanitize All User Inputs **Do This:** Implement rigorous input validation and sanitization to prevent injection attacks (e.g., SQL injection, XSS). **Don't Do This:** Trust user input without validation or sanitization. **Why:** Untrusted input can be manipulated to execute malicious code, access unauthorized data, or disrupt the application. **Code Example (Validation with "zod"):** """typescript import { z } from "zod"; const userSchema = z.object({ username: z.string().min(3).max(20), // Basic validation email: z.string().email(), // Email format validation age: z.number().int().positive().optional(), // Optional and numeric validation password: z.string().min(8).max(50), // Password length validation }); async function createUser(req: Request) { try { const body = await req.json(); const validatedUser = userSchema.parse(body); // Throws error if invalid // Now "validatedUser" is guaranteed to conform to the schema. console.log("Validated User:", validatedUser); return new Response(JSON.stringify({ message: "User created successfully", user: validatedUser }), { status: 201, headers: { "Content-Type": "application/json", }, }); } catch (error: any) { console.error("Validation Error:", error.errors); return new Response(JSON.stringify({ message: "Validation failed", errors: error.errors }), { status: 400, headers: { "Content-Type": "application/json", }, }); } } //Example usage with Bun's HTTP server Bun.serve({ fetch(req) { if (req.url.endsWith('/users')) { return createUser(req); } return new Response("Not Found.", { status: 404 }); }, port: 3000, }); console.log("Server running on port 3000"); """ **Explanation:** * **"zod"**: A TypeScript-first schema declaration and validation library. It ensures data conforms to the schema *before* it's used. * **"userSchema.parse(body)"**: Attempts to parse the request body against the schema. If parsing fails, it throws an error, preventing invalid data from reaching the core application logic. Error handling is crucial to prevent crashes and provide informative messages. * **Benefits**: Improved robustness, prevention of common injection attacks, and type safety within the Bun application. **Anti-Pattern:** Directly using request data without any form of validation. ### 1.2 Standard: Context-Aware Output Encoding/Escaping **Do This:** Ensure that data is encoded or escaped correctly when outputting to different contexts (HTML, JSON, URLs). **Don't Do This:** Output data directly without considering the context. **Why:** Prevents Cross-Site Scripting (XSS) attacks. **Code Example (HTML Escaping):** """typescript function escapeHTML(str: string): string { return str.replace(/[&<>'"]/g, (tag) => ({ '&': '&', '<': '<', '>': '>', "'": ''', '"': '"' }[tag] || tag)); } async function displayComment(comment: string) { const escapedComment = escapeHTML(comment); // Imagine inserting escapedComment into a HTML template const htmlOutput = "<div>${escapedComment}</div>"; // This is for demonstration in Bun. Normally you'd insert this into a full template return new Response(htmlOutput, { headers: { 'Content-Type': 'text/html; charset=utf-8', }, }); } Bun.serve({ fetch(req) { const url = new URL(req.url); const comment = url.searchParams.get('comment') || 'No comment provided'; return displayComment(comment); }, port: 3000, }); console.log("Server running on port 3000"); """ **Explanation:** * **"escapeHTML" function:** Replaces potentially dangerous characters with their HTML entities, ensuring they are rendered as text and not interpreted as code. * **Context-Aware**: The encoding logic is tied to the output context (HTML in this case). Different encoding is required for JSON, URLs, etc. * **Why it matters:** Escaping prevents malicious code from being injected into the HTML, mitigating XSS attacks. **Anti-Pattern:** Directly inserting user-provided strings into HTML without escaping. ## 2. Authentication and Authorization ### 2.1 Standard: Secure Authentication Mechanisms **Do This:** Use robust authentication mechanisms like multi-factor authentication (MFA), strong password policies, and secure session management. Leverage established libraries and frameworks for authentication. **Don't Do This:** Implement custom authentication logic without thoroughly understanding security implications. **Why:** Protect user accounts and prevent unauthorized access. **Code Example (Authentication using "elysiajs" with "jwt" plugin):** """typescript import { Elysia } from "elysia"; import { jwt } from "@elysiajs/jwt"; import { staticPlugin } from '@elysiajs/static' const app = new Elysia() .use(staticPlugin()) //serve static files from "public" .use( jwt({ name: 'jwt', secret: process.env.JWT_SECRET || "super_secret", // Replace with a strong, environment-specific secret }) ) .decorate("verifyToken", function(this: { jwt: any }, request:Request) { const authHeader = request.headers.get("Authorization"); if (!authHeader) { return null; // No token provided } const token = authHeader.split(" ")[1]; // Extract token part try{ return this.jwt.verify(token) } catch(err){ return null; } }) .derive(({ jwt, verifyToken }) => ({ isLoggedIn: (request:Request) => { return !!verifyToken(request) }, })) .get("/protected", ({ isLoggedIn }) => { if (!isLoggedIn) { // This relies on derive, creating the 'isLoggedIn' function/property return new Response("Unauthorized", { status: 401 }); } return "Protected Resource"; }) .post("/login", async ({ jwt, request }) => { //Assume we're using JSON for simplicity const body = await request.json() // In a real application, you'd validate credentials against the DB if (body.username === "test" && body.password === "password") { const token = await jwt.sign({ username: "test", id: 123 }); return { token: token }; } return new Response("Invalid credentials", { status: 401 }); }) .listen(3000); console.log("Elysia app is running at ${app.server?.hostname}:${app.server?.port}"); """ **Explanation:** * **"@elysiajs/jwt":** Elysia plugin for JWT (JSON Web Token) authentication. * **"jwt.sign()":** Creates a JWT token with user information. Store only necessary, non-sensitive information in the token. * **"jwt.verify()":** Verifies the token's validity and decodes its payload. * **"process.env.JWT_SECRET":** The JWT secret key should *never* be hardcoded. It must be stored in an environment variable. * **"isLoggedIn" derive:** Illustrates how to easily create helper functions/properties to check user authentication status across routes using Elysia's "derive" feature. * **Authorization Header**: Note how the "Authorization" header is inspected for the token. The "Bearer" scheme is standard practice. **Anti-Patterns:** * Hardcoding secrets in the code. * Using weak hashing algorithms. * Storing plain-text passwords. * Rolling your own cryptography (unless you're an expert). * Not validating that the token belongs to the current user. ### 2.2 Standard: Role-Based Access Control (RBAC) **Do This:** Implement RBAC to control access to resources based on user roles. **Don't Do This:** Grant all users the same level of access. **Why:** Prevent unauthorized users from accessing sensitive data or performing privileged operations. **Code Example (RBAC with Elysia decorators):** """typescript import { Elysia } from "elysia"; import { jwt } from "@elysiajs/jwt"; type UserRole = "admin" | "editor" | "viewer"; interface User { id: number; username: string; role: UserRole; } //In production, the User data would come from database const users: User[] = [ { id: 1, username: "admin", role: "admin" }, { id: 2, username: "editor", role: "editor" }, { id: 3, username: "viewer", role: "viewer" } ]; const app = new Elysia() .use( jwt({ name: 'jwt', secret: "super_secret", // Replace with a strong, environment-specific secret }) ) .decorate("authenticate", async ({ request, jwt }) => { // Validate JWT and retrieve User const authHeader = request.headers.get("Authorization"); if (!authHeader) return null; const token = authHeader.split(" ")[1]; try { const decoded = await jwt.verify(token) as {username: string}; if (!decoded) return null; const user = users.find(user => user.username === decoded.username); return user || null; } catch (err) { return null; } }) .decorate("authorize", (user: User | null, requiredRole: UserRole) => { // Check if User has required role if (!user || user.role !== requiredRole) { return false; } return true; }) .get("/admin", async ({ authenticate, authorize }) => { const user = await authenticate(); if (!user || !authorize(user, "admin")) { return new Response("Unauthorized", { status: 403 }); } return "Admin Resource"; }) .get("/editor", async ({ authenticate, authorize }) => { const user = await authenticate(); if (!user || !authorize(user, "editor")) { return new Response("Unauthorized", { status: 403 }); } return "Editor Resource"; }) .listen(3000); console.log("Elysia app is running at ${app.server?.hostname}:${app.server?.port}"); """ **Explanation:** * **"authenticate" Decorator**: Authenticates the user using the JWT. * **"authorize" Decorator**: Implements the actual role-based access control logic. It checks if the authenticated user has the required role for the requested resource. * **Role Assignment**: Users are assigned roles (e.g., "admin", "editor", "viewer"). * **Resource Protection**: Access to each route ("admin," "editor") is protected based on the required role. * **"403 Forbidden"**: Returns a 403 Forbidden status code if the user lacks the necessary permissions **Anti-Patterns:** * Relying solely on client-side role checks. * Hardcoding roles in the code. * Using overly permissive access controls. ## 3. Data Protection ### 3.1 Standard: Encryption of Sensitive Data at Rest and in Transit **Do This:** Encrypt sensitive data both at rest (stored in databases, files) and in transit (during network communication). **Don't Do This:** Store sensitive data in plain text or transmit it over unencrypted channels. **Why:** Protect data from unauthorized access and interception. **Code Example (Encryption with "node:crypto" and HTTPS):** """typescript import { createCipheriv, createDecipheriv, randomBytes } from 'node:crypto'; import fs from 'node:fs'; import { mkdir } from 'node:fs/promises' const algorithm = 'aes-256-cbc'; // Key should be 32 bytes long const key = randomBytes(32); // Initialization vector should be 16 bytes long const iv = randomBytes(16); async function encryptFile(filePath: string, encryptedFilePath: string) { const plaintext = fs.readFileSync(filePath, 'utf8'); const cipher = createCipheriv(algorithm, key, iv); let encrypted = cipher.update(plaintext, 'utf8', 'hex'); encrypted += cipher.final('hex'); // Prepend IV to encrypted data, to be used for decryption const ivPrependedData = iv.toString('hex') + encrypted; await mkdir('encrypted', { recursive: true }) fs.writeFileSync(encryptedFilePath, ivPrependedData); } async function decryptFile(encryptedFilePath: string, decryptedFilePath: string) { const encryptedData = fs.readFileSync(encryptedFilePath, 'utf8'); // Extract the IV from the beginning of the encrypted data. const extractedIv = Buffer.from(encryptedData.slice(0, 32), 'hex'); const encryptedContent = encryptedData.slice(32); const decipher = createDecipheriv(algorithm, key, extractedIv); let decrypted = decipher.update(encryptedContent, 'hex', 'utf8'); decrypted += decipher.final('utf8'); await mkdir('decrypted', { recursive: true }) fs.writeFileSync(decryptedFilePath, decrypted); } const filePath = 'plaintext/my_secret.txt'; const encryptedFilePath = 'encrypted/my_secret.enc'; const decryptedFilePath = 'decrypted/my_secret.txt'; await encryptFile(filePath, encryptedFilePath); console.log('File encrypted successfully!'); await decryptFile(encryptedFilePath, decryptedFilePath); console.log('File decrypted successfully!'); """ """typescript //HTTPS example import { TLSSocket } from 'node:tls' import { readFileSync } from 'node:fs' import { request } from 'node:https' // Example self-signed certificate const options = { key: readFileSync('./key.pem'), cert: readFileSync('./cert.pem'), rejectUnauthorized: false, } Bun.serve({ async fetch(req) { if (req.url === "/data"){ return new Response(JSON.stringify({secure:"I am encrypted"}), {headers: {"Content-Type": "application/json"}}) } return new Response("Not Found", { status: 404 }) }, tls: options, port: 3001, }) console.log("Secure server listening on port 3001") """ **Explanation:** * **"node:crypto":** The built-in crypto module for encryption and decryption. * **"aes-256-cbc":** A strong encryption algorithm. * **Key Generation:** Keys should be randomly generated. Securely store and manage the encryption keys (e.g. using a dedicated key management system). *Do not* hardcode keys to the source code! * **HTTPS:** Use HTTPS to encrypt data in transit between the client and server. Obtain valid TLS/SSL certificates from a Certificate Authority. * **Importance of IV**: A unique initialization vector (IV) is used for each encryption operation to ensure that even if the same plaintext is encrypted multiple times, the resulting ciphertext will be different. This prevents attacks like ciphertext pattern analysis. The IV must be transmitted with the ciphertext, but it does not need to be kept secret. The prepended "iv" to the encrypted output allows for decryption later. **Anti-Patterns:** * Using weak encryption algorithms (e.g., DES, MD5). * Storing encryption keys in the code. * Disabling HTTPS. * Not rotating encryption keys regularly. ### 3.2 Standard: Secure Data Storage **Do This:** Securely store data in databases and files, following the principle of least privilege. Implement appropriate access controls. **Don't Do This:** Store data in publicly accessible locations or grant unnecessary permissions. **Why:** Prevent unauthorized data breaches. **Recommendations:** * Use parameterized queries to prevent SQL injection. * Store sensitive data in encrypted form. * Regularly back up data. * Implement data retention policies. * Audit data access. ## 4. Error Handling and Logging ### 4.1 Standard: Secure Error Handling **Do This:** Implement secure error handling to prevent information leakage. Avoid displaying sensitive information in error messages. **Don't Do This:** Display stack traces or detailed error messages to end-users. **Why:** Protect sensitive data from being exposed through error messages. **Code Example (Custom Error Handler):** """typescript async function riskyOperation(input: number) { if (input < 0) { throw new Error("Input must be non-negative"); } // Risky operation... return input * 2; } async function handleRequest(req: Request): Promise<Response> { try { const url = new URL(req.url); const inputParam = url.searchParams.get('input'); if (!inputParam) { throw new Error("Input parameter 'input' is required"); } const input = parseInt(inputParam, 10); if (isNaN(input)) { throw new Error("Invalid input: 'input' must be a number"); } const result = await riskyOperation(input); return new Response("Result: ${result}"); } catch (error: any) { console.error("An error occurred:", error); // Log the full error for debugging. Important for your internal tracing. return new Response(JSON.stringify({ error: "An unexpected error occurred. Please try again later." }), { status: 500, headers: { "Content-Type": "application/json" } }); // Generic, user-friendly error message. } } Bun.serve({ fetch: handleRequest, port: 3000, }); console.log("Server listening on port 3000"); """ **Explanation:** * **"try...catch":** Handles potential errors during code execution. * **Generic Error Message:** Returns a generic error message to the user. * **Logging:** Logs the full error details internally for debugging purposes *without* exposing them to the client. **Anti-Patterns:** * Displaying stack traces to end-users. * Returning sensitive data in error messages. * Not logging errors. ### 4.2 Standard: Comprehensive Logging **Do This:** Implement comprehensive logging to track application activity, including authentication attempts, access to resources, and data modifications while respecting privacy regulations. Use structured logging format such as JSON. **Don't Do This:** Log sensitive data in plain text or skip logging important events. **Why:** Provide an audit trail for security investigations and debugging. **Code Example (Logging with "pino"):** """typescript import pino from 'pino' const logger = pino({ level: 'info', // Set the logging level (e.g., debug, info, warn, error) - configurable through ENV formatters: { level: (label) => { return { level: label }; }, }, timestamp: pino.stdTimeFunctions.isoTime, //ISO 8601 timestamp }) async function processRequest(req: Request) { try { const url = new URL(req.url); const userId = url.searchParams.get('userId'); const resourceId = url.searchParams.get('resourceId'); logger.info({ event: 'resource_access', httpMethod: req.method, path: url.pathname, userId, resourceId, message: 'User accessed resource.', }); // Structured logging of event. return new Response("Accessed resource"); } catch (error: any) { logger.error({ event: 'request_error', error: error.message, stack: error.stack, //Include stack trace for internal debugging message: 'An error occurred during request processing.', }); return new Response("An Error Occurred", { status: 500 }); } } Bun.serve({ fetch: processRequest, port: 3000, }); console.log("Server running on port 3000"); """ **Explanation:** * **"pino":** A fast and production-ready logging library. * **Structured Logging (JSON):** Logs are formatted as JSON objects, making them easy to parse and analyze. * **Logging Levels:** "pino" supports different logging levels (debug, info, warn, error), allowing you to control the verbosity of the logs. * **Auditing and Monitoring:** Use log aggregation and monitoring tools (e.g., ElasticSearch, Kibana, Grafana) to analyze logs and detect security incidents. **Anti-Patterns:** * Logging sensitive data without proper masking. * Not logging enough information. * Using unstructured logging formats (making logs hard to parse). * Storing logs in publicly accessible locations. ## 5. Dependency Management ### 5.1 Standard: Keep Dependencies Up-to-Date **Do This:** Regularly update dependencies to patch security vulnerabilities. Use tools to automate dependency updates (e.g., Dependabot). **Don't Do This:** Use outdated dependencies that haven't received security updates. **Why:** Ensure that known vulnerabilities in dependencies are addressed promptly. **Practice:** * Use "bun update" command regularly. * Automate dependency updates with tools like Dependabot. * Monitor security advisories for dependencies. * Use "bun audit" to check for known vulnerabilities. ### 5.2 Standard: Use Secure Dependency Sources **Do This:** Only install dependencies from trusted sources. Verify the integrity of downloaded packages (e.g., using checksums). **Don't Do This:** Install dependencies from unknown or untrusted sources. **Why:** Prevent malicious code from being injected into your application through compromised dependencies. ## 6. Security Headers ### 6.1 Standard: Implement Security Headers **Do This:** Configure appropriate security headers to protect against common web attacks. **Don't Do This:** Omit security headers, leaving the application vulnerable to attacks. **Code Example (Setting Security Headers in Elysia):** """typescript import { Elysia } from "elysia"; const app = new Elysia() .onRequest(({ set }) => { // Prevent Clickjacking set.headers['X-Frame-Options'] = 'DENY'; // Prevent MIME-sniffing vulnerabilities set.headers['X-Content-Type-Options'] = 'nosniff'; // Enable Cross-Site Scripting (XSS) filter set.headers['X-XSS-Protection'] = '1; mode=block'; // Content Security Policy (CSP) - Customize as needed set.headers['Content-Security-Policy'] = "default-src 'self'"; //Strict Transport Security (HSTS) set.headers['Strict-Transport-Security'] = 'max-age=31536000; includeSubDomains; preload'; }) .get("/", () => "Hello Elysia") .listen(3000); console.log("Elysia app is running at ${app.server?.hostname}:${app.server?.port}"); """ **Explanation:** * **"X-Frame-Options: DENY":** Prevents clickjacking attacks by preventing the page from being embedded in a frame. * **"X-Content-Type-Options: nosniff":** Prevents MIME-sniffing vulnerabilities, where the browser attempts to guess the content type of a resource, potentially executing malicious code. * **"X-XSS-Protection: 1; mode=block":** Enables the browser's built-in XSS filter. * **"Content-Security-Policy":** The most critical header. Defines a whitelist of sources from which the browser is allowed to load resources. This is crucial for preventing XSS attacks. The example shows a restrictive policy ("default-src 'self'"). You'll need to customize this based on your application's needs. * **"Strict-Transport-Security":** Enforces HTTPS connections, preventing man-in-the-middle attacks. "max-age" sets the duration in seconds for which the browser should remember to only access the site over HTTPS. "includeSubDomains" applies the policy to all subdomains, and "preload" allows your site to be included in the HSTS preload list in browsers. **Importance of CSP:** * **Customize it correctly!** A badly configured CSP can break the application. Use a tool to generate and validate CSP policies * **Reporting:** Set up CSP reporting to monitor violations and refine the rule set. **Anti-Patterns:** * Not setting security headers at all. * Using overly permissive CSP policies. * Not testing security header configurations. ## 7. Denial of Service (DoS) Prevention ### 7.1 Standard: Implement Rate Limiting **Do This:** Implement rate limiting to prevent abuse and DoS attacks. **Don't Do This:** Allow unlimited requests from a single IP address or user. **Why:** Protect the application from being overwhelmed by malicious traffic. **Code Example (Rate limiting with "elysiajs" plugin and redis):** """typescript import { Elysia } from "elysia"; import { rateLimit } from 'elysia-rate-limit'; const app = new Elysia() .use(rateLimit({ max: 100, // Allow 100 requests per 10 seconds per IP duration: 10000, // 10 Second in milliseconds })) .get("/", () => "Hello Elysia") .listen(3000); console.log("Elysia app is running at ${app.server?.hostname}:${app.server?.port}"); """ **Explanation:** * **"elysia-rate-limit":** Install with "bun add elysia-rate-limit". An elysia plugin for rate limiting. * **"max"**: The maximum number of requests allowed within the specified "duration". * **"duration"**: The time window in milliseconds. * **Custom key**: Use custom key resolvers (custom way to generate/resolve keys for storing requests) * **Alternative Storage**: Redis and other storages are encouraged for distributed system and higher performance **Anti-Patterns:** * Not implementing rate limiting. * Using overly permissive rate limits. * Relying solely on client-side rate limiting. ### 7.2 Standard: Input Size Limits **Do This:** Enforce limits on the size of request bodies and query parameters to prevent resource exhaustion attacks. **Don't Do This:** Accept arbitrarily large inputs. **Why:** Prevent attackers from overwhelming the server with excessive data. **Code Example (Body Limit with Elysia):** """typescript import { Elysia } from "elysia"; import { error } from 'elysia' const MAX_JSON_SIZE = 1024 * 1024; // 1MB const app = new Elysia() .onParse((context) => { if(context.request.headers.get('content-length') as any > MAX_JSON_SIZE) { return error(413, "Request too large") } }) .post("/upload", async ({ request }) => { //Example upload endpoint const body = await request.json() console.log(body) return "Uploaded" }) .listen(3000); console.log("Elysia app is running at ${app.server?.hostname}:${app.server?.port}"); """ **Explanation:** * **"MAX_JSON_SIZE"**: Constant that defines the maximum payload can be. * **"content-length" Header:** Checks the "content-length" header of the request. This is set by the client and indicates the size of the request body. * **"onParse" Hook**: Elysia's "onParse" hook that runs *before* any parsing is done. * **Error 413**: 413 Payload Too Large is the server's response. **Anti-Patterns:** * Not setting input size limits at all. * Setting the input size limit too high. * Not providing meaningful errors to the user. ## 8. Regular Security Audits and Penetration Testing ### 8.1 Standard: Conduct Regular Security Audits **Do This:** Perform regular security audits to identify vulnerabilities and ensure compliance with security standards. Use automated tools and manual reviews. **Don't Do This:** Neglect security audits. **Why:** Proactively identify and address security weaknesses. ### 8.2 Standard: Penetration Testing **Do This:** Engage external security experts to perform penetration testing to simulate real-world attacks. **Don't Do This:** Rely solely on internal security assessments. **Why:** Obtain an unbiased assessment of the application's security posture.
# Deployment and DevOps Standards for Bun This document outlines the standards and best practices for deploying and managing Bun applications in production environments. It covers build processes, CI/CD pipelines, and operational considerations specific to Bun's runtime and ecosystem. These guidelines ensure maintainability, performance, security, and efficient resource utilization. ## 1. Build Processes and CI/CD Pipelines ### 1.1. Standard: Utilize a Consistent Build Process **Do This:** Define a standardized build process that ensures consistency across different environments. Use a tool like "bun build" or a custom script managed through "package.json" scripts. **Don't Do This:** Rely on manual build steps or environment-specific configurations not captured in the codebase. **Why:** Consistent builds guarantee that the same code produces the same artifacts regardless of the environment. This eliminates "works on my machine" issues and simplifies debugging. **Example:** """json // package.json { "scripts": { "build": "bun build ./src/index.ts --outfile ./dist/index.js --format esm" }, "devDependencies": { "bun-types": "latest" } } """ **Explanation:** This "package.json" defines a "build" script that uses "bun build" to compile the TypeScript file "src/index.ts" into an ES module bundle located at "dist/index.js". Using a script ensures everyone on the team and the CI/CD system use the same command. ### 1.2. Standard: Implement CI/CD Pipelines **Do This:** Automate the build, test, and deployment process using a CI/CD pipeline (e.g., GitHub Actions, GitLab CI, CircleCI). **Don't Do This:** Manually deploy applications or skip automated testing. **Why:** CI/CD pipelines increase development velocity, reduce errors, and enable faster releases. **Example (GitHub Actions):** """yaml # .github/workflows/ci-cd.yml name: CI/CD Pipeline on: push: branches: [ main ] pull_request: branches: [ main ] jobs: build: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - name: Setup Bun uses: oven-sh/setup-bun@v1 with: bun-version: latest - name: Install Dependencies run: bun install - name: Run Tests run: bun test - name: Build run: bun run build deploy: needs: build runs-on: ubuntu-latest if: github.ref == 'refs/heads/main' steps: - uses: actions/checkout@v3 - name: Deploy to Production run: | # Replace with your deployment script (e.g., SSH to server, deploy to cloud platform) echo "Deploying to Production..." # Example: scp -r ./dist user@server:/var/www/my-app """ **Explanation:** This GitHub Actions workflow defines two jobs: "build" and "deploy". The "build" job checks out the code, sets up Bun, installs dependencies, runs tests, and builds the application. The "deploy" job runs only when code is pushed to the "main" branch. It includes a placeholder deployment script. This should be replaced with appropriate deployment commands for your infrastructure. ### 1.3. Standard: Utilize Semantic Versioning **Do This:** Adhere to Semantic Versioning (SemVer) for packages published to a registry. Bump the version number using "bun version" or similar tools. **Don't Do This:** Use arbitrary versioning schemes or neglect versioning altogether. **Why:** SemVer provides a clear contract for consumers of your packages, making it easier to manage dependencies and prevent breaking changes. **Example:** """bash bun version patch # Increment patch version (e.g., 1.0.0 -> 1.0.1) bun version minor # Increment minor version (e.g., 1.0.0 -> 1.1.0) bun version major # Increment major version (e.g., 1.0.0 -> 2.0.0) """ ### 1.4. Standard: Leverage Bun's Native Capabilities for Performance Optimization **Do This:** Utilize Bun's native support for ES modules, TypeScript, and JSX to optimize build times and reduce bundle sizes. Explore using "bun link" for local development and dependency management. **Don't Do This:** Overly rely on transpilation tools when Bun offers native support. **Why:** Bun's speed comes from leveraging Zig and efficient execution. Using its native capabilities avoids unnecessary overhead. **Example:** Instead of: """bash // Using babel or other transpilers extensively "build": "babel src --out-dir dist" """ Prefer: """bash // bun build leverages native support "build": "bun build src/index.ts --outfile dist/index.js --format esm" """ ### 1.5 Standard: Containerize Bun Applications with Docker **Do This:** Use Docker to create consistent and isolated environments for building and running Bun applications. **Don't Do This:** Deploy directly to bare metal without containerization for production environments. **Why:** Docker provides reproducibility, portability, and isolation, simplifying deployment and management. **Example (Dockerfile):** """dockerfile FROM oven/bun:latest WORKDIR /app COPY package*.json ./ RUN bun install COPY . . EXPOSE 3000 CMD ["bun", "run", "start"] """ **Explanation:** This Dockerfile: - Starts from the official "oven/bun:latest" base image. - Sets the working directory to "/app". - Copies "package.json" and "bun.lockb" to the working directory. - Installs the application's dependencies using "bun install". - Copies the rest of the application source code. - Exposes port 3000. - Starts the application using "bun run start". The "start" script should be defined in your "package.json". ## 2. Production Considerations ### 2.1. Standard: Monitor Application Health **Do This:** Implement comprehensive application monitoring using tools like Prometheus, Grafana, or cloud provider monitoring services (e.g., AWS CloudWatch, Google Cloud Monitoring). Monitor key metrics like CPU usage, memory usage, response times, and error rates. **Don't Do This:** Rely solely on log files for detecting issues or ignore performance metrics. **Why:** Proactive monitoring allows you to identify and resolve issues before they impact users. **Example (Prometheus Metrics):** """typescript // metrics.ts import { Histogram } from 'prom-client'; const httpRequestDurationMicroseconds = new Histogram({ name: 'http_request_duration_seconds', help: 'Duration of HTTP requests in seconds', labelNames: ['method', 'route', 'status'], buckets: [0.1, 0.3, 0.5, 0.7, 1, 3, 5, 7, 10] // Example buckets }); export { httpRequestDurationMicroseconds }; """ """typescript // server.ts import { httpRequestDurationMicroseconds } from './metrics'; app.get('/hello', (req, res) => { const end = httpRequestDurationMicroseconds.startTimer(); //... your logic ... res.send('Hello, world!'); end({ method: req.method, route: '/hello', status: res.statusCode }); }); """ **Explanation:** This code snippet defines a Prometheus histogram metric to track the duration of HTTP requests. It uses the "prom-client" library. The "startTimer()" function returns an "end" function which should be called at the end of the request lifecycle, passing in labels for request details. You'd also need to set up an endpoint to expose these metrics in a format Prometheus can scrape (using "prom-client"). ### 2.2. Standard: Implement Logging and Error Tracking **Do This:** Use a centralized logging system (e.g., ELK stack, Graylog, Sentry) to collect and analyze application logs. Implement structured logging with appropriate log levels (debug, info, warn, error). Use an error tracking service (e.g., Sentry, Bugsnag) to capture and report exceptions. **Don't Do This:** Write log messages directly to the console in production or ignore unhandled exceptions. **Why:** Centralized logging and error tracking significantly simplify troubleshooting and debugging in production. Structured logs allow for easier querying and analysis. **Example:** """typescript // logger.ts import pino from 'pino'; const logger = pino({ level: process.env.LOG_LEVEL || 'info', // Use environment variable for log level transport: { target: 'pino-pretty', // Optional: For human-readable output in development options: { translateTime: 'SYS:standard', ignore: 'pid,hostname' } } }); export default logger; // In your application: import logger from './logger'; try { // some code that might throw an error } catch (error) { logger.error({ error }, 'An unexpected error occurred'); // Structured logging } """ **Explanation:** This example uses the "pino" library for logging. It configures the log level based on the "LOG_LEVEL" environment variable. It also uses "pino-pretty" library to display in easier to read format when running locally. The structured logging allows you to easily create alerts and metrics based on log properties. ### 2.3. Standard: Secure Sensitive Configuration Data **Do This:** Store sensitive configuration data (API keys, database passwords, etc.) in environment variables or a secure configuration management system (e.g., HashiCorp Vault, AWS Secrets Manager). **Don't Do This:** Hardcode sensitive data in the codebase or store it in plain text configuration files. **Why:** Protecting sensitive configuration data is crucial for security. Environment variables are a basic but effective way to manage this. More robust solutions like Vault provide enhanced security features. **Example (.env file):** """ DATABASE_URL=postgres://user:password@host:port/database API_KEY=your_secret_api_key """ **Example (Accessing environment variables in Bun):** """typescript const databaseUrl = process.env.DATABASE_URL; const apiKey = process.env.API_KEY; if (!databaseUrl || !apiKey) { console.error('Error: Missing required environment variables.'); process.exit(1); } """ ### 2.4. Standard: Implement Health Checks and Readiness Probes **Do This:** Expose health check and readiness probe endpoints for your application. These endpoints allow orchestrators (e.g., Kubernetes) to monitor the application's health and readiness to accept traffic. Utilize Bun's fast startup time to expedite readiness. **Don't Do This:** Assume the application is always healthy or rely solely on basic ping checks. **Why:** Health checks and readiness probes improve application availability and resilience. **Example:** """typescript // health.ts app.get('/health', (req, res) => { // Perform checks (e.g., database connection, external service availability) const isHealthy = true; // Replace with actual health check logic if (isHealthy) { res.writeHead(200, { 'Content-Type': 'application/json' }); res.end(JSON.stringify({ status: 'ok' })); } else { res.writeHead(503, { 'Content-Type': 'application/json' }); res.end(JSON.stringify({ status: 'error', message: 'Service unavailable' })); } }); app.get('/ready', (req, res) => { // Perform checks if app is ready to receive traffic const isReady = true; // Replace with actual readiness check logic if (isReady) { res.writeHead(200, { 'Content-Type': 'application/json' }); res.end(JSON.stringify({ status: 'ready' })); } else { res.writeHead(503, { 'Content-Type': 'application/json' }); res.end(JSON.stringify({ status: 'not ready', message: 'Service not ready' })); } }); """ **Explanation:** This example defines two endpoints: "/health" and "/ready". The "/health" endpoint performs basic health checks (e.g., database connection, external service availability). The "/ready" endpoint checks if the application is ready to accept traffic. These endpoints would be used by Kubernetes or other orchestrators. ### 2.5. Standard: Utilize Bun's "bun pm" for Package Management **Do This:** Use "bun pm" or "bun install" to manage dependencies in production environments. Leverage "bun.lockb" to ensure consistent dependency versions. **Don't Do This:** Mix package managers or neglect the lockfile. **Why:** Consistent dependency versions across environments are critical for stability. "bun pm" is optimized for speed and compatibility with the Bun runtime. ### 2.6 Standard: Implement Graceful Shutdowns **Do This:** Implement graceful shutdown handling to allow the application to finish processing existing requests before terminating. Handle signals like "SIGTERM" and "SIGINT". **Don't Do This:** Abruptly terminate the application without allowing ongoing requests to complete. **Why:** Graceful shutdowns prevent data loss, ensure that requests are completed successfully, and improve the user experience. **Example:** """typescript // server.ts let server = app.listen(process.env.PORT || 3000, () => { console.log("Server started"); }); process.on('SIGTERM', shutdown); process.on('SIGINT', shutdown); function shutdown() { console.log('Received shutdown signal, shutting down gracefully...'); server?.close((err) => { if (err) { console.error('Error closing server:', err); process.exit(1); } console.log('Server closed.'); // Perform cleanup tasks (e.g., close database connections) console.log('Cleanup tasks completed. Exiting.'); process.exit(0); }); // Forcefully terminate after a timeout to prevent indefinite hanging setTimeout(() => { console.error('Could not close connections in time, forcefully shutting down'); process.exit(1); }, 15000); // 15 seconds } """ **Explanation:** This code sets up signal handlers for "SIGTERM" and "SIGINT". When a shutdown signal is received, the "shundown" function gracefully closes the server. It also establishes a timeout in case closing the connection takes to long, exiting after the timeout. ## 3. Applying Principles Specifically to Bun ### 3.1. Zero-Config Builds. **Do This:** Consider Bun’s strengths. It’s designed for speed and requires less configuration. Aim to minimize build step customization, letting Bun do what it does best. **Don't Do This:** Overcomplicate builds introducing tooling that offsets Bun built in speed. ### 3.2. Take Advantage of "bun test" and "bun fmt".** **Do This:** Integrate "bun test" (Bun's built-in test runner that's compatible with Jest, Mocha, and more) and "bun fmt" (Bun's code formatter) in your CI/CD pipeline. **Don't Do This:** Rely only on external testing or formatting tools. **Why:** These streamline testing and code formatting tasks. """yaml # GitHub Actions example using bun test and bun fmt name: Bun CI on: [push] jobs: test: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - uses: oven-sh/setup-bun@v1 with: bun-version: latest - run: bun install - run: bun test # Uses bun's test runner - run: bun fmt --check # Checks formatting """ ### 3.3. Leverage Bun Shell for Simplified Scripting **Do This** Use Bun shell in "package.json" (or similar) for simpler cross-platform scripting. **Don't Do This:** Overly complicate scripts or use external tools when Bun shell would suffice within "package.json". """json { "scripts": { "deploy": "bun x some-deployment-tool" } } """ ## 4. Common Anti-Patterns and Mistakes to Avoid * **Long Build Times:** If builds are slow, investigate dependencies, optimize code, and leverage caching mechanisms in CI/CD. Run the Bun profiler for further details. * **Lack of Monitoring:** Deploying without proper monitoring is a recipe for disaster. Always implement comprehensive monitoring to track application health and performance. * **Hardcoded Secrets:** Never hardcode sensitive information in the codebase. Use environment variables or a secrets management system. * **Inconsistent Environments:** Ensure that development, staging, and production environments are as similar as possible to avoid environment-specific issues. For instance, use Docker or other containerization technologies. By adhering to these standards and best practices, development teams can build, deploy, and manage Bun applications effectively in production environments. This leads to improved stability, performance, security, and maintainability.
# Core Architecture Standards for Bun This document outlines core architectural standards for Bun projects, focusing on project structure, fundamental patterns, and organization principles to ensure maintainability, performance, and security. These standards are designed to leverage the latest features of Bun and promote consistency across development teams. ## 1. Project Structure and Organization A well-defined project structure greatly enhances maintainability and collaboration. Bun projects should adhere to a modular and component-based organization. ### 1.1. Standard Directory Layout **Do This:** Adopt a consistent directory structure across all Bun projects. A recommended structure is: """ bun-project/ ├── src/ # Source code directory │ ├── components/ # Reusable, independent UI components │ ├── models/ # Data models and schemas │ ├── services/ # Business logic and external API interactions │ ├── utils/ # Utility functions and helpers │ ├── types/ # TypeScript type definitions │ ├── app.ts # Main application entry point │ └── router.ts # Route definitions (if using a custom router) ├── public/ # Static assets (HTML, CSS, images) ├── test/ # Unit and integration tests │ ├── components/ │ ├── models/ │ └── services/ ├── .env # Environment variables (using "bunfig.toml" is preferred) ├── bun.lockb # Dependency lockfile ├── bunfig.toml # Bun configuration file ├── README.md # Project documentation └── tsconfig.json # TypeScript configuration """ **Don't Do This:** Use a flat or haphazard directory structure. Avoid placing all files in a single "src/" directory without logical separation. **Why:** Promotes modularity, easy navigation, and clear separation of concerns. Consistent structure simplifies onboarding for new developers. **Example:** A server-side rendered React component within this structure: """ bun-project/ └── src/ └── components/ └── UserProfile.tsx """ """typescript // src/components/UserProfile.tsx import React from 'react'; interface UserProfileProps { username: string; bio: string; } const UserProfile: React.FC<UserProfileProps> = ({ username, bio }) => { return ( <div> <h2>{username}</h2> <p>{bio}</p> </div> ); }; export default UserProfile; """ ### 1.2. Modular Design **Do This:** Break down the application into self-contained modules (components, services, etc.). Favor small, focused modules over large, monolithic ones. Use ES modules ("import"/"export") for dependency management. **Don't Do This:** Create tightly coupled modules with excessive dependencies on each other. Avoid global state and side effects as much as possible. **Why:** Modularity improves code reusability, testability, and maintainability. It also allows for easier parallel development and feature isolation. **Example:** Example of using ES modules: """typescript // src/services/UserService.ts const fetchUser = async (id: string) => { const response = await fetch("https://api.example.com/users/${id}"); return await response.json(); }; export { fetchUser }; """ """typescript // src/components/UserProfile.tsx import { fetchUser } from '../services/UserService'; // ... component logic using fetchUser """ ### 1.3. Configuration Management **Do This:** Utilize "bunfig.toml" for project configuration. Store environment-specific variables in ".env" files (or, better yet, use a secrets management system for production). Access environment variables using "Bun.env". **Don't Do This:** Hardcode configuration values directly in the source code. Commit ".env" files to version control (especially those containing secrets). **Why:** Decouples configuration from code, allowing different environments (development, staging, production) to be easily managed without code changes. Using "bunfig.toml" allows for specific bun configurations. **Example:** "bunfig.toml" configuration: """toml # bunfig.toml debug = true port = 3000 [test] database_url = "postgresql://user:password@host:port/testdb" """ """typescript // src/app.ts const debugMode = Bun.env.DEBUG === 'true'; const port = parseInt(Bun.env.PORT || '3000'); console.log("Running in debug mode: ${debugMode}"); console.log("Server listening on port ${port}"); """ ### 1.4 Typescript Usage **Do This**: Employ TypeScript extensively to catch type-related errors early. Create and utilise types to increase the readibility and reliablity of your code. **Don't Do This**: Avoid introducing "any" types without due cause, as this reduces the benfits offered by typescript. **Why**: Typescript enables earlier error checking, increased reliabilty, and improved readibility. **Example:** """typescript interface User { id: number, name: string, email: string } async function getUser(url : string) : Promise<User> { const response = await fetch(url); const user = await response.json() as User; return user; } """ ## 2. Architectural Patterns Choosing the appropriate architectural pattern dramatically impacts an application’s characteristics. Given Bun's strengths, certain patterns are well-suited. ### 2.1. Layered Architecture **Do This:** Organize the application into distinct layers (e.g., presentation, application, domain, infrastructure). Each layer should have a specific responsibility and only depend on the layers below it. **Don't Do This:** Create circular dependencies between layers or allow layers to bypass intermediate layers. **Why:** Layered architecture promotes separation of concerns, making it easier to understand, test, and modify different parts of the application independently. **Example:** * **Presentation Layer:** Handles user interface and input/output. (React components, API endpoints) * **Application Layer:** Orchestrates the application logic, invoking services and coordinating data flow. (Route handlers, controller logic) * **Domain Layer:** Contains the core business logic and domain entities. (Business rules, validation logic) * **Infrastructure Layer:** Provides access to external resources (databases, APIs, file system). (Database connections, API clients) A simplified example: """typescript // src/presentation/UserController.ts import { UserService } from '../application/UserService'; class UserController { private userService: UserService; constructor(userService: UserService) { this.userService = userService; } async getUser(req: Request, res: Response) { const userId = req.params.id; const user = await this.userService.getUserById(userId); res.json(user); } } // src/application/UserService.ts import { UserRepository } from '../domain/UserRepository'; class UserService { private userRepository: UserRepository; constructor(userRepository: UserRepository) { this.userRepository = userRepository; } async getUserById(id: string) { return this.userRepository.findById(id); } } // src/domain/UserRepository.ts interface UserRepository { findById(id: string): Promise<User | null>; } // src/infrastructure/PostgresUserRepository.ts import { UserRepository } from '../domain/UserRepository'; class PostgresUserRepository implements UserRepository { async findById(id: string) { // Database query using Bun's SQLite or PostgreSQL support // Example depends on the chosen database library return { id, name: 'Example User' }; // Replace with actual implementation } } """ ### 2.2. Microservices Architecture (Considerations) **Do This:** If the application is complex and needs to scale independently, consider a microservices architecture. Each microservice should handle a specific business capability. Use asynchronous communication (e.g., message queues) to decouple services. **Don't Do This:** Start with a microservices architecture for simple applications. Avoid tight coupling between microservices (e.g., shared databases). **Why:** Microservices allow for independent deployment, scaling, and technology choices. Provides fault isolation if one microservice fails. **Example:** Consider a simple e-commerce application. Separate microservices could handle: * **Product Catalog:** Manages product information. * **Order Management:** Handles order creation and processing. * **User Authentication:** Manages user accounts and authentication. These microservices could communicate via HTTP APIs or a message queue like Redis or RabbitMQ. ### 2.3. API Gateway **Do This:** Use an API gateway to route requests to the appropriate microservice, handle authentication, and provide other cross-cutting concerns (e.g., rate limiting, logging). Consider using Bun.serve to create a simple API gateway. **Don't Do This:** Expose microservices directly to the outside world without a gateway. **Why:** The API gateway provides a single entry point for the application, simplifying client interactions and improving security. **Example:** """typescript // api-gateway.ts async function handleRequest(req: Request): Response { const url = new URL(req.url); const pathname = url.pathname; if (pathname.startsWith("/products")) { // Forward to product catalog return await fetch("http://product-catalog-service:3001" + pathname, { method: req.method, headers: req.headers, body: req.body, }); } else if (pathname.startsWith("/orders")) { // Forward to order management service return await fetch("http://order-management-service:3002" + pathname, { method: req.method, headers: req.headers, body: req.body, }); } else { return new Response("Not Found", { status: 404 }); } } Bun.serve({ port: 3000, fetch: handleRequest, }); """ ## 3. Coding Practices Adhering to consistent coding practices enhances code readability, maintainability, and collaboration. ### 3.1. Immutability **Do This:** Favor immutable data structures and operations. Use "const" for variables that should not be reassigned. Avoid mutating arrays and objects directly; use methods like "map", "filter", "reduce", and the spread operator ("...") to create new instances. **Don't Do This:** Modify data structures in place. Rely on mutable state, especially in concurrent scenarios. **Why:** Immutability simplifies reasoning about code, prevents unexpected side effects, and improves performance in certain cases. **Example:** """typescript // Mutable approach (bad) const numbers = [1, 2, 3]; numbers.push(4); // Mutates the original array console.log(numbers); // [1, 2, 3, 4] // Immutable approach (good) const numbers2 = [1, 2, 3]; const newNumbers = [...numbers2, 4]; // Creates a new array console.log(numbers2); // [1, 2, 3] console.log(newNumbers); // [1, 2, 3, 4] """ ### 3.2. Asynchronous Programming **Do This:** Use "async/await" for asynchronous operations. Handle errors using "try/catch" blocks. Leverage Bun's built-in async APIs (e.g., "Bun.file()", "Bun.serve()"). Utilize Promises for handling asynchronous results and rejections. **Don't Do This:** Rely on callbacks or nested callbacks (callback hell). Ignore potential errors in asynchronous operations. **Why:** "async/await" makes asynchronous code easier to read and reason about. Proper error handling prevents unhandled exceptions and improves application stability. **Example:** """typescript // Using async/await async function readFile(filePath: string): Promise<string> { try { const file = Bun.file(filePath); const text = await file.text(); return text; } catch (error) { console.error("Error reading file: ${error}"); throw error; // Re-throw the error to be handled upstream } } // Calling the function readFile('my-file.txt') .then(content => console.log(content)) .catch(error => console.error('Failed to read file')); """ ### 3.3 Concurrent programming **Do This:** Use the "Bun.spawn" API, or worker threads to run your code concurrently. **Don't Do This** Block the main thread of your application. **Why:** Concurrent programming allows for better application responsiveness, increased throughput, and improved resource utilization. **Example** """typescript //spawn example const { stdout, stderr, exited } = Bun.spawn(['ls', '-l']); const output = await new Response(stdout).text(); console.log(output); """ ### 3.4. Error Handling **Do This:** Implement robust error handling using "try...catch" blocks for synchronous code and ".catch()" for Promises. Log errors with sufficient context (e.g., timestamp, error message, stack trace). Use custom error classes to provide more specific error information. **Don't Do This:** Swallow errors without logging or handling them. Rely on default error messages without providing context. **Why:** Proper error handling prevents application crashes, provides valuable debugging information, and improves user experience. **Example:** """typescript class CustomError extends Error { constructor(message: string, public code: string) { super(message); this.name = 'CustomError'; } } async function processData(data: any) { try { if (!data) { throw new CustomError('Data is null or undefined', 'DATA_INVALID'); } // ... process data } catch (error: any) { console.error("Error processing data: ${error.message}", { code: error.code, stack: error.stack, timestamp: new Date().toISOString(), }); throw error; // Re-throw the error for upstream handling } } """ ## 4. Security Best Practices Building secure applications is paramount. These guidelines focus on minimizing common vulnerabilities in Bun environments. ### 4.1. Input Validation **Do This:** Validate all user inputs to prevent injection attacks (e.g., SQL injection, XSS). Sanitize inputs before using them in queries or displaying them in the UI. Use established validation libraries (e.g., Joi, validator.js). **Don't Do This:** Trust user inputs without validation. Directly concatenate user inputs into SQL queries or HTML. **Why:** Input validation prevents malicious users from injecting arbitrary code or data into the application. **Example:** """typescript //Using zod for validation: import { z } from "zod"; const UserSchema = z.object({ username: z.string().min(3).max(20), email: z.string().email(), password: z.string().min(8), }); function createUser(userData: any) { try { const validatedData = UserSchema.parse(userData); // ... create user in database using validatedData } catch (error: any) { console.error("Validation error:", error.errors); throw error; } } """ ### 4.2. Authentication and Authorization **Do This:** Implement secure authentication mechanisms (e.g., JWT, OAuth 2.0) to verify user identities. Use authorization to control access to resources based on user roles and permissions. Store passwords securely using bcrypt or Argon2. **Don't Do This:** Store passwords in plain text. Implement custom authentication logic without using established security protocols. Grant excessive permissions to users. **Why:** Authentication and authorization protect sensitive data and prevent unauthorized access to application resources. **Example:** """typescript // Authenticating a user and creating a JWT. This is simple and should be expanded upon utilizing secure environment // variables and error handling import { sign } from 'jsonwebtoken'; async function loginUser(username: string, password: string) { //Query user in DB with details const payload = { username: username, id: '123' } const token = await sign(payload, 'shhhhh'); return token; } """ ### 4.3. Dependency Management **Do This:** Regularly update dependencies to patch security vulnerabilities. Use "bun audit" or "npm audit" to identify and fix known vulnerabilities. Lock dependencies using "bun.lockb" (or "package-lock.json" with npm) to ensure consistent builds. **Don't Do This:** Use outdated dependencies with known security vulnerabilities. Ignore security audit warnings. **Why:** Keeping dependencies up-to-date mitigates the risk of exploiting known vulnerabilities in third-party libraries. ### 4.4. Secrets Management **Do This:** Store sensitive information (e.g., API keys, database passwords) in environment variables or a dedicated secrets management system (e.g., HashiCorp Vault, AWS Secrets Manager). Never commit secrets to version control. **Don't Do This:** Hardcode secrets in the source code or configuration files. Store secrets in easily accessible locations (e.g., ".env" files in production). **Why:** Secure secrets management protects sensitive information from unauthorized access. ## 5. Performance Optimization Bun is designed for performance. Maximize its potential by following these guidelines. ### 5.1. Code Splitting **Do This:** Use code splitting to reduce the initial load time of the application. Load only the necessary code for each page or feature. **Don't Do This:** Bundle the entire application into a single large JavaScript file. **Why:** Code splitting improves application performance by reducing the amount of code that needs to be downloaded and parsed initially. **Example:** Dynamic imports in React: """typescript // Example inside a React component import React, { useState, useEffect } from 'react'; function MyComponent() { const [module, setModule] = useState<any>(null); useEffect(() => { import('./HeavyComponent') // This triggers code splitting .then(HeavyComponent => { setModule(() => HeavyComponent.default); }); }, []); if (!module) { return <div>Loading...</div>; } const HeavyComponent = module; return <HeavyComponent />; } export default MyComponent; """ ### 5.2. Caching **Do This**: Implement caching strategies to store frequently accessed data in memory or on disk. Use HTTP caching headers to leverage browser caching. Consider using a dedicated caching service like Redis or Memcached for more complex caching requirements. Leverage Bun's efficient file system APIs for disk-based caching. **Don't Do This:** Cache sensitive data without proper security measures. Invalidate the cache frequently or never, leading to stale data. **Why:** Caching reduces latency and improves application responsiveness by serving data from a cache instead of fetching it from a slower data source. **Example:** Basic in-memory caching: """typescript const cache = new Map(); async function getData(key: string, fetchFunction: () => Promise<any>) { if (cache.has(key)) { console.log('Serving from cache'); return cache.get(key); } const data = await fetchFunction(); cache.set(key, data); return data; } async function fetchFromAPI() { //Simulate data fetching: await new Promise(r => setTimeout(r, 2000)); return {data : 'Fetched Data!'}; } async function main() { const result = await getData('myKey', fetchFromAPI); console.log(result); const cachedResult = await getData('myKey', fetchFromAPI); console.log(cachedResult); } main(); """ ### 5.3. Efficient Data Structures and Algorithms **Do this:** Utilise appropriate data structures and effective algorithms. **Don't do this:** Ignore the time complexity of your algorithms. **Why:** Efficient data structures allow for faster program execution. ## 6. Testing Thorough testing is crucial for identifying and fixing bugs early in the development process. ### 6.1. Unit Tests **Do This:** Write unit tests for individual modules and functions to verify their correctness in isolation. Use a testing framework like Jest, Mocha, or Vitest (Vitest often integrates well with a Bun-based workflow). Aim for high code coverage. **Don't Do This:** Skip unit tests or write superficial tests that don't cover all edge cases. **Why:** Unit tests provide fast feedback on code changes and prevent regressions. **Example:** Jest unit test: """typescript //Example add function: function add(a : number, b : number) : number { return a + b; } // add.test.ts import { add } from './add'; describe('add', () => { it('should return the sum of two numbers', () => { expect(add(1, 2)).toBe(3); expect(add(-1, 1)).toBe(0); expect(add(0, 0)).toBe(0); }); }); """ ### 6.2. Integration Tests **Do This:** Write integration tests to verify the interaction between different modules or services. Test the integration with external APIs and databases. **Don't Do This:** Skip integration tests or rely solely on unit tests. **Why:** Integration tests ensure that the different parts of the application work together correctly. ### 6.3. End-to-End Tests **Do This** Write end-to-end tests to simulate user interactions and verify the application's functionality from the user's perspective. Use tools like Playwright or Cypress. **Don't Do This:** Skip end-to-end tests or rely solely on unit and integration tests. **Why:** End-to-end tests provide confidence that the application works as expected in a real-world environment. ## 7. Documentation Comprehensive documentation is essential for maintainability and knowledge sharing. ### 7.1. Code Comments **Do This:** Write clear and concise comments to explain complex logic, algorithms, and design decisions. Follow a consistent commenting style. **Don't Do This:** Write unnecessary comments that state the obvious or repeat the code. **Why:** Code comments help other developers (and your future self) understand the code more easily. ### 7.2. API Documentation **Do This:** Generate API documentation using tools like JSDoc or Swagger/OpenAPI. Describe the purpose, parameters, and return values of each API endpoint. **Don't Do This:** Skip API documentation or write incomplete documentation. **Why:** API documentation makes it easier for other developers to consume the API. ### 7.3. README **Do This**: Write a comprehensive README file for each project. Include: Project description, how to get started (installation, configuration), how to run tests and how to deploy. **Don't Do This:** Leave the README empty, or include a basic description. **Why:** Improves project usability. By adhering to these core architecture standards, Bun projects can be built to be more maintainable, performant, and secure. This guide should be considered a living document, subject to updates as Bun evolves and best practices emerge.
# Tooling and Ecosystem Standards for Bun This document outlines the coding standards and best practices related to tooling and ecosystem usage when developing with Bun. Adhering to these standards will result in more maintainable, performant, and secure applications. ## 1. Package Management and Dependencies ### 1.1. Bun's Native Package Manager Bun includes its own native package manager, which is significantly faster than "npm" or "yarn". **Do This:** * Use "bun install" for installing dependencies. * Use "bun add <package>" to add new dependencies. * Use "bun remove <package>" to remove dependencies. **Don't Do This:** * Avoid using "npm" or "yarn" unless absolutely necessary due to specific package compatibility issues. **Why:** Bun's package manager is optimized for speed and efficiency within the Bun runtime, leading to faster installation times and reduced overhead. **Example:** """bash # Install dependencies bun install # Add a new dependency bun add lodash # Remove a dependency bun remove lodash """ ### 1.2. Dependency Versions **Do This:** * Use semantic versioning (semver) ranges (e.g., "^1.2.3" or "~1.2.3") in "package.json" to allow for minor and patch updates. * Regularly update dependencies using "bun update". * Use "bun lockb" to ensure deterministic builds across environments. **Don't Do This:** * Avoid using exact versions (e.g., "1.2.3") unless absolutely necessary to prevent unexpected breaking changes. * Avoid directly modifying "bun.lockb"—use "bun update" instead. **Why:** Semver ranges allow for automatic updates within compatible versions, while "bun lockb" ensures that all environments use the same dependency versions, preventing inconsistencies. **Example:** """json // package.json { "dependencies": { "lodash": "^4.17.0", "express": "~4.18.0" } } """ """bash # Update dependencies bun update # Create lockfile bun lockb """ ### 1.3. Development Dependencies **Do This:** * Use "--dev" flag when installing development-only dependencies (e.g., testing libraries, linters). * Group development dependencies under the "devDependencies" section in "package.json". **Don't Do This:** * Avoid installing development dependencies as regular dependencies as this increases the production bundle size. **Why:** Separating development dependencies ensures that these tools are not included in the production build, reducing its size and improving performance. **Example:** """bash # Install a development dependency bun add --dev eslint // package.json { "devDependencies": { "eslint": "^8.0.0" } } """ ## 2. Transpilation and Bundling ### 2.1. Native TypeScript Support Bun has built-in support for TypeScript, so no extra configuration is required in most cases. **Do This:** * Leverage Bun's native TypeScript support for faster transpilation. * Use "tsconfig.json" to customize TypeScript compilation options. * Use ".ts", ".tsx" extensions for Typescript files. **Don't Do This:** * Avoid using external tools for basic TypeScript transpilation unless you need very specific transformations. * Avoid unnecessary type assertions or "any" types, strive for full type coverage and safety. **Why:** Native support eliminates the overhead of external transpilers, reducing build times and simplifying the development process. TypeScript enforces stronger type checking, reducing runtime errors and improving code quality. **Example:** """typescript // index.ts function greet(name: string): string { return "Hello, ${name}!"; } console.log(greet("Bun")); // tsconfig.json { "compilerOptions": { "target": "esnext", "module": "esnext", "moduleResolution": "node", "esModuleInterop": true, "strict": true, "jsx": "react-jsx" // If using React } } // running the code bun index.ts """ ### 2.2. Bundling with Bun Bun can also act as a bundler similar to webpack or esbuild. **Do This:** * Use "bun build" to bundle your application for production. * Configure bundling options in "bunfig.toml" or through command-line arguments. * Leverage tree shaking by using ES module syntax to eliminate dead code. * Consider code splitting where appropriate for larger applications to reduce initial load times. **Don't Do This:** * Avoid including unnecessary files or dependencies in the bundle. * Overcomplicate bundling configurations; leverage Bun defaults where possible. **Why:** Bundling optimizes code for deployment by minimizing file sizes and improving loading times. **Example:** """toml # bunfig.toml [build] entryPoints = ["./src/index.ts"] outdir = "./dist" minify = true format = "esm" """ """bash # Build the application bun build """ ### 2.3. ES Modules **Do This:** * Use ES modules ("import" and "export") for modularizing code. * Ensure files have ".js", ".ts", ".jsx", or ".tsx" extensions when using ES modules. **Don't Do This:** * Avoid using CommonJS ("require") unless interfacing with legacy code. * Mix ES modules and CommonJS in the same file. **Why:** ES modules are the modern standard for JavaScript modules, offering better static analysis and tree-shaking capabilities. Bun natively supports ES modules, providing seamless integration and performance benefits. **Example:** """typescript // utils.ts export function add(a: number, b: number): number { return a + b; } // index.ts import { add } from './utils'; console.log(add(2, 3)); """ ## 3. Linting and Formatting ### 3.1. ESLint and Prettier **Do This:** * Integrate ESLint for linting and Prettier for code formatting. * Use a shared configuration file (e.g., ".eslintrc.js", ".prettierrc.js") to enforce consistent coding styles. * Configure ESLint and Prettier to run automatically on file save or commit using IDE extensions or Git hooks. **Don't Do This:** * Ignore linting or formatting errors. * Use inconsistent coding styles across the codebase. **Why:** Linting and formatting tools ensure code consistency and help catch potential errors early in the development process, improving code readability and maintainability. **Example:** """bash # Install eslint and prettier bun add --dev eslint prettier eslint-config-prettier eslint-plugin-prettier # .eslintrc.js module.exports = { extends: ['eslint:recommended', 'plugin:prettier/recommended'], env: { node: true, es6: true, }, parserOptions: { ecmaVersion: 2020, sourceType: 'module', }, rules: {}, }; // .prettierrc.js module.exports = { semi: true, trailingComma: 'es5', singleQuote: true, printWidth: 100, }; """ ### 3.2. Editor Configuration **Do This:** * Configure your code editor (e.g., VS Code) to automatically format code on save. * Install ESLint and Prettier extensions for real-time linting and formatting feedback. * Use editorconfig to maintain consistent coding styles among different editors. **Don't Do This:** * Rely solely on manual formatting. * Ignore editor warnings or suggestions. **Why:** Editor integration automates the linting and formatting process, ensuring that code is always consistent and error-free. **Example:** """json // .editorconfig root = true [*] charset = utf-8 end_of_line = lf insert_final_newline = true trim_trailing_whitespace = true indent_style = space indent_size = 2 """ ## 4. Testing ### 4.1. Testing Frameworks **Do This:** * Use a testing framework like Jest, Mocha, or Vitest for writing unit and integration tests. * Write tests for all critical functionality. * Use clear and descriptive test names. * Aim for high test coverage. * Consider using Bun's built-in test runner through "bun test". **Don't Do This:** * Skip writing tests for complex or critical logic. * Write overly complex or brittle tests. * Rely solely on manual testing. **Why:** Automated tests ensure that code behaves as expected and help prevent regressions when making changes. **Example:** """bash # Using Bun's test runner // math.test.ts or math.test.js import { expect, test } from 'bun:test'; import { add } from './math'; test('adds 1 + 2 to equal 3', () => { expect(add(1, 2)).toBe(3); }); // Run tests bun test """ ### 4.2. Test-Driven Development (TDD) **Do This:** * Consider using TDD to write tests before implementing the actual code. * Follow the red-green-refactor cycle. **Don't Do This:** * Write tests only after the code is complete. **Why:** TDD helps ensure that code is testable and meets the specified requirements. ## 5. Debugging ### 5.1. Debugging Tools **Do This:** * Use Bun's built-in debugger or integrate with VS Code's debugger. * Use "console.log" or "console.debug" statements sparingly for debugging purposes. * Use breakpoints to step through code execution. **Don't Do This:** * Leave debugging statements in production code. * Rely solely on trial-and-error debugging. **Why:** Debugging tools help identify and fix errors quickly and efficiently. **Example:** """bash # Debugging with VS Code // launch.json { "version": "0.2.0", "configurations": [ { "type": "bun", "request": "launch", "name": "Debug Bun Script", "program": "${workspaceFolder}/index.ts", "cwd": "${workspaceFolder}" } ] } """ ### 5.2. Logging **Do This:** * Use a logging library like "pino" or "winston" for structured logging; if "console.log" is used, keep it consistent. * Log important events, errors, and warnings. * Use different log levels (e.g., debug, info, warn, error) to categorize log messages. * Implement centralized logging for handling and analysis on production systems. **Don't Do This:** * Log sensitive information (e.g., passwords, API keys). * Over-log, creating excessive noise in the logs. **Why:** Logging provides valuable insights into the application's behavior and helps diagnose issues in production environments. **Example:** """bash bun add pino """ """typescript // logger.ts import pino from 'pino'; const logger = pino({ level: process.env.LOG_LEVEL || 'info', }); export default logger; // app.ts import logger from './logger'; logger.info('Application started'); """ ## 6. Environment Variables and Configuration ### 6.1. Environment Variables **Do This:** * Use environment variables for configuring the application (e.g., API keys, database URLs). * Load environment variables from a ".env" file using a library like "dotenv" or "env-var". * Validate environment variables at application startup. **Don't Do This:** * Hardcode configuration values in the code. * Store sensitive information in version control. **Why:** Environment variables allow for easy configuration changes without modifying the code and help keep sensitive information secure. **Example:** """bash # Install env-var bun add env-var # .env API_KEY=your_api_key PORT=3000 """ """typescript // config.ts import { env } from 'env-var'; const config = { apiKey: env('API_KEY').required().asString(), port: env('PORT').default(3000).asPortNumber(), }; export default config; // app.ts import config from './config'; console.log(config.apiKey); console.log("Server listening on port ${config.port}"); """ ### 6.2. Configuration Files For more complex configuration, use structured configuration files such as TOML or YAML. **Do This:** * Use TOML for Bun configurations (e.g., "bunfig.toml"). * Implement validation and schema for configuration files. **Don't Do This:** * Mix configuration types unnecessarily. * Avoid clear documentation for each setting. **Why:** Structured configuration files offer improved readability and maintainability, especially for complex configurations. ## 7. Code Documentation ### 7.1. JSDoc **Do This:** * Use JSDoc comments to document functions, classes, and modules. * Include descriptions, parameter types, and return types in JSDoc comments. * Generate API documentation using tools like TypeDoc. **Don't Do This:** * Skip documenting code, especially complex or public-facing APIs. * Write vague or incomplete documentation. **Why:** Code documentation makes it easier for developers to understand and use the code, improving collaboration and maintainability. **Example:** """typescript /** * Adds two numbers together. * * @param {number} a - The first number. * @param {number} b - The second number. * @returns {number} The sum of the two numbers. */ export function add(a: number, b: number): number { return a + b; } """ ## 8. Security ### 8.1. Dependency Security **Do This:** * Regularly scan dependencies for known vulnerabilities using "bun audit". * Update vulnerable dependencies to the latest secure versions. * Use a Software Bill of Materials (SBOM) to keep track of the dependencies. **Don't Do This:** * Ignore vulnerability warnings. * Use outdated or unmaintained dependencies. **Why:** Dependency vulnerabilities can expose the application to security risks. Regularly scanning and updating dependencies helps mitigate these risks. **Example:** """bash # Audit dependencies bun audit """ ### 8.2. Input Validation **Do This:** * Validate all user inputs to prevent injection attacks. * Sanitize inputs before using them in database queries or rendering them in the UI. * Use a library like "joi" or "zod" for input validation. **Don't Do This:** * Trust user inputs without validation. * Pass unsanitized inputs directly to database queries. **Why:** Input validation prevents malicious users from injecting code or data into the application, protecting it from security threats. ### 8.3. Secrets Management **Do This**: * Store API keys, database passwords, and other secrets using an environment variable manager like Doppler or Hashicorp Vault. * Avoid hardcoding sensitive credentials directly in the application code. **Why**: Keeping secrets separate from your code is an important security measure. ## 9. Performance Optimization ### 9.1. Profiling Tools **Do This:** * Use Bun's profiling tools to identify performance bottlenecks. * Measure the performance of critical code paths. * Optimize slow code paths using techniques like caching, memoization, or algorithmic improvements. **Don't Do This:** * Make performance optimizations without measuring their impact. * Optimize prematurely. **Why:** Profiling tools provide insights into the application's performance and help identify areas that can be improved. ### 9.2. Code Splitting **Do This:** * Use code splitting to break up large bundles into smaller chunks that can be loaded on demand. * Use dynamic imports to load modules asynchronously. **Don't Do This:** * Create overly granular chunks, which can increase the number of HTTP requests. **Why:** Code splitting reduces the initial load time of the application and improves its responsiveness. ## 10. Collaboration and Version Control ### 10.1. Git **Do This:** * Use Git for version control. * Create meaningful commit messages. * Use branches for developing new features or fixing bugs. * Follow a consistent branching strategy (e.g., Gitflow). * Review code before merging it into the main branch. **Don't Do This:** * Commit directly to the main branch without review. * Commit large changes without breaking them into smaller, more manageable commits. * Commit sensitive information (e.g., passwords, API keys) to version control. **Why:** Version control allows for tracking changes to the codebase, collaborating with other developers, and reverting to previous versions if necessary. ### 10.2. Code Reviews **Do This:** * Conduct thorough code reviews before merging changes. * Focus on code quality, security, and performance during code reviews. * Provide constructive feedback. **Why:** Code reviews help catch errors, improve code quality, and share knowledge among team members. By adhering to these coding standards, you can ensure that your Bun projects are maintainable, performant, and secure. This, in turn, fosters consistent and high-quality code across your development team.