Integrating TypeScript with Serverless Architectures
Understand best practices for deploying TypeScript applications in serverless environments to enhance scalability and reduce operational overhead.
0 likes
12 views
Rule Content
--- description: Enforce best practices for deploying TypeScript applications in serverless environments to enhance scalability and reduce operational overhead. globs: ['**/*.ts'] tags: [typescript, serverless, best-practices] priority: 1 version: 1.0.0 --- # Integrating TypeScript with Serverless Architectures ## Context - Applicable when developing and deploying TypeScript applications using serverless platforms such as AWS Lambda, Azure Functions, or Google Cloud Functions. - Aims to ensure code quality, maintainability, and efficient resource utilization in serverless environments. ## Requirements 1. **Use Explicit Type Annotations** - Annotate variables, function parameters, and return types explicitly to improve code readability and catch errors early. 2. **Implement Robust Error Handling** - Anticipate and catch potential exceptions within serverless functions. - Log meaningful error messages and return appropriate error responses to clients. - Use retry mechanisms to handle transient failures like network timeouts. - Implement circuit breakers to prevent cascading failures. 3. **Optimize Function Performance** - Minimize function execution time by optimizing code and using efficient algorithms. - Allocate appropriate memory sizes to functions, avoiding both over- and under-provisioning. - Implement caching mechanisms to reduce redundant computations and function invocations. 4. **Manage Dependencies Effectively** - Use tools like Webpack to bundle and minify code, reducing package size and improving cold start times. - Avoid using global variables to prevent unpredictable behavior across function invocations. 5. **Follow the Principle of Least Privilege** - Grant functions the minimal amount of access they need by defining specific IAM roles for each function. - Avoid using wildcards in IAM role statements to enhance security. 6. **Configure Dead Letter Queues (DLQs) for Asynchronous Functions** - Set up DLQs to capture events that fail processing, ensuring no data is lost and facilitating debugging. 7. **Implement Logging and Monitoring** - Configure log retention policies to manage storage costs and comply with data retention requirements. - Use monitoring tools to track function performance, error rates, and resource utilization. 8. **Use Linting and Formatting Tools** - Integrate ESLint with TypeScript to catch potential issues early and enforce coding standards. - Use Prettier to ensure consistent code formatting across the project. ## Examples <example> // Good: Explicit type annotations const userName: string = "Alice"; function greet(name: string): string { return `Hello, ${name}!`; } </example> <example type="invalid"> // Bad: Implicit types const userName = "Alice"; function greet(name) { return `Hello, ${name}!`; } </example> <example> // Good: Robust error handling with retry mechanism async function fetchData(url: string): Promise<Data> { let attempts = 0; const maxAttempts = 3; while (attempts < maxAttempts) { try { const response = await fetch(url); if (!response.ok) throw new Error('Network response was not ok'); return await response.json(); } catch (error) { attempts++; if (attempts >= maxAttempts) { console.error('Failed to fetch data:', error); throw error; } } } } </example> <example type="invalid"> // Bad: No error handling async function fetchData(url: string): Promise<Data> { const response = await fetch(url); return await response.json(); } </example> <example> // Good: Optimized function with appropriate memory allocation export const handler: Handler = async (event) => { // Function logic here }; handler.memorySize = 256; // Allocate 256 MB of memory </example> <example type="invalid"> // Bad: Over-allocated memory export const handler: Handler = async (event) => { // Function logic here }; handler.memorySize = 1024; // Allocate 1024 MB of memory unnecessarily </example> <example> // Good: Using Webpack to bundle dependencies // webpack.config.js module.exports = { entry: './src/index.ts', output: { filename: 'bundle.js', path: __dirname + '/dist', }, resolve: { extensions: ['.ts', '.js'], }, module: { rules: [ { test: /\.ts$/, use: 'ts-loader', exclude: /node_modules/, }, ], }, }; </example> <example type="invalid"> // Bad: Deploying with unbundled dependencies // No Webpack configuration </example> <example> # Good: Specific IAM role for a function functions: myFunction: handler: handler.myFunction iamRoleStatements: - Effect: "Allow" Action: - "dynamodb:Query" Resource: "arn:aws:dynamodb:us-east-1:123456789012:table/MyTable" </example> <example type="invalid"> # Bad: Overly permissive IAM role functions: myFunction: handler: handler.myFunction iamRoleStatements: - Effect: "Allow" Action: "*" Resource: "*" </example> <example> # Good: Configuring a Dead Letter Queue functions: myFunction: handler: handler.myFunction events: - sns: topicName: myTopic deadLetterQueue: targetArn: arn:aws:sqs:us-east-1:123456789012:myDLQ type: sqs </example> <example type="invalid"> # Bad: No Dead Letter Queue configured functions: myFunction: handler: handler.myFunction events: - sns: topicName: myTopic </example> <example> // Good: ESLint configuration for TypeScript { "extends": ["eslint:recommended", "plugin:@typescript-eslint/recommended"], "parser": "@typescript-eslint/parser", "plugins": ["@typescript-eslint"], "rules": { "semi": ["error", "always"], "quotes": ["error", "single"] } } </example> <example type="invalid"> // Bad: No ESLint configuration </example>