Instruction file imported from bradcstevens/github-copilot-agent-mode-starter-kit (
.github/instructions/technologies/aiohttp.instructions.md). Copyright stays with the author.
Aiohttp Best Practices
This document provides a comprehensive guide to aiohttp development, covering code organization, performance, security, testing, and deployment.
Library Information:
- Name: aiohttp
- Tags: web, python, http-client, async
1. Code Organization and Structure
1.1. Directory Structure Best Practices:
- Project Root:
src/: Contains the main application code.main.py: Entry point of the application.app.py: Application factory and setup.routes.py: Defines application routes.handlers/: Contains request handlers.user_handlers.py: User-related handlers.product_handlers.py: Product-related handlers.
middlewares/: Custom middleware components.logging_middleware.py: Logging middleware.auth_middleware.py: Authentication middleware.
utils/: Utility modules.db.py: Database connection and utilities.config.py: Configuration management.
tests/: Contains unit and integration tests.conftest.py: Pytest configuration file.unit/: Unit tests.integration/: Integration tests.
static/: Static files (CSS, JavaScript, images).templates/: Jinja2 or other template files.docs/: Project documentation.requirements.txt: Python dependencies.Dockerfile: Docker configuration file.docker-compose.yml: Docker Compose configuration..env: Environment variables.README.md: Project description and instructions..gitignore: Specifies intentionally untracked files that Git should ignore..github/instructions/: Project specific GitHub AI instructions.
1.2. File Naming Conventions:
- Python files:
snake_case.py(e.g.,user_handlers.py,database_utils.py). - Class names:
CamelCase(e.g.,UserHandler,DatabaseConnection). - Function names:
snake_case(e.g.,get_user,create_product). - Variables:
snake_case(e.g.,user_id,product_name). - Constants:
UPPER_SNAKE_CASE(e.g.,DEFAULT_PORT,MAX_CONNECTIONS).
1.3. Module Organization:
- Group related functionality into modules.
- Use clear and descriptive module names.
- Avoid circular dependencies.
- Keep modules focused and concise.
- Use packages to organize modules into a hierarchical structure.
1.4. Component Architecture:
- Layered Architecture: Separate the application into distinct layers (e.g., presentation, business logic, data access).
- Microservices Architecture: Decompose the application into small, independent services.
- Hexagonal Architecture (Ports and Adapters): Decouple the application core from external dependencies.
- MVC (Model-View-Controller): Organize the application into models (data), views (presentation), and controllers (logic).
1.5. Code Splitting Strategies:
- Route-based splitting: Load modules based on the requested route.
- Feature-based splitting: Divide the application into feature modules.
- Component-based splitting: Split the application into reusable components.
- On-demand loading: Load modules only when they are needed.
- Asynchronous loading: Use
asyncio.gatheror similar techniques to load modules concurrently.
2. Common Patterns and Anti-patterns
2.1. Design Patterns:
- Singleton: For managing shared resources like database connections or configuration objects.
- Factory: For creating instances of classes with complex initialization logic.
- Strategy: For implementing different algorithms or behaviors.
- Observer: For implementing event-driven systems.
- Middleware: For handling cross-cutting concerns like logging, authentication, and error handling.
2.2. Recommended Approaches for Common Tasks:
- Request Handling: Use request handlers to process incoming requests.
- Routing: Use
aiohttp.web.RouteTableDeffor defining routes. - Middleware: Implement middleware for request pre-processing and response post-processing.
- Data Serialization: Use
aiohttp.web.json_responsefor serializing data to JSON. - Error Handling: Implement custom error handlers to handle exceptions gracefully.
- Session Management: Use
aiohttp-sessionfor managing user sessions. - WebSockets: Utilize
aiohttp.web.WebSocketResponsefor handling WebSocket connections.
2.3. Anti-patterns and Code Smells:
- Creating a new
ClientSessionfor each request: This is a performance bottleneck. Reuse a singleClientSession. - Blocking operations in asynchronous code: Avoid using blocking operations (e.g.,
time.sleep) in asynchronous code. - Ignoring exceptions: Always handle exceptions properly to prevent unexpected behavior.
- Overusing global variables: Avoid using global variables as much as possible to maintain code clarity and testability.
- Tight coupling: Decouple components to improve maintainability and reusability.
- Hardcoding configuration: Use environment variables or configuration files to manage configuration settings.
2.4. State Management:
- Application State: Store application-level state in the
aiohttp.web.Applicationinstance. - Request State: Store request-specific state in the
aiohttp.web.Requestinstance. - Session State: Use
aiohttp-sessionto manage user session data. - Database: Use a database like PostgreSQL, MySQL, or MongoDB to store persistent state.
- Redis/Memcached: Use in-memory data stores for caching frequently accessed data.
2.5. Error Handling:
- Use
try-exceptblocks: Wrap code that may raise exceptions intry-exceptblocks. - Handle specific exceptions: Catch specific exception types instead of using a generic
except Exception. - Log exceptions: Log exceptions with detailed information for debugging.
- Return informative error responses: Return appropriate HTTP status codes and error messages to the client.
- Implement custom error handlers: Create custom error handlers to handle specific exception types.
- Use
aiohttp.web.HTTPException: Raiseaiohttp.web.HTTPExceptionto return HTTP error responses.
3. Performance Considerations
3.1. Optimization Techniques:
- Reuse
ClientSession: Always reuse a singleClientSessioninstance for making multiple requests. - Connection Pooling: aiohttp automatically uses connection pooling, so reuse your session.
- Keep-Alive Connections: Keep-alive connections are enabled by default, reducing connection overhead.
- Gzip Compression: Enable Gzip compression for responses to reduce bandwidth usage.
- Caching: Implement caching for frequently accessed data to reduce database load.
- Optimize Database Queries: Optimize database queries to improve response times.
- Use Indexes: Use indexes in your database tables to speed up queries.
- Limit Payload Size: Keep request and response payloads as small as possible.
- Background Tasks: Use
asyncio.create_taskto offload long-running tasks to the background. - Profiling: Use profiling tools to identify performance bottlenecks.
3.2. Memory Management:
- Avoid Memory Leaks: Ensure that all resources are properly released to prevent memory leaks.
- Use Generators: Use generators to process large datasets in chunks.
- Limit Object Creation: Minimize the creation of objects to reduce memory overhead.
- Use Data Structures Efficiently: Choose appropriate data structures to optimize memory usage.
- Garbage Collection: Understand how Python's garbage collection works and optimize your code accordingly.
3.3. Rendering Optimization:
- Template Caching: Cache templates to reduce rendering time.
- Minimize Template Logic: Keep template logic simple and move complex logic to request handlers.
- Use Asynchronous Templates: Use asynchronous template engines like
aiohttp-jinja2. - Optimize Static Files: Optimize static files (CSS, JavaScript, images) to reduce page load times.
3.4. Bundle Size Optimization:
- Minimize Dependencies: Reduce the number of dependencies in your project.
- Tree Shaking: Use tree shaking to remove unused code from your bundles.
- Code Minification: Minify your code to reduce bundle sizes.
- Code Compression: Compress your code to further reduce bundle sizes.
3.5. Lazy Loading:
- Lazy Load Modules: Load modules only when they are needed.
- Lazy Load Images: Load images only when they are visible in the viewport.
- Use Asynchronous Loading: Use
asyncio.gatheror similar techniques to load resources concurrently.
4. Security Best Practices
4.1. Common Vulnerabilities:
- SQL Injection: Prevent SQL injection by using parameterized queries or an ORM.
- Cross-Site Scripting (XSS): Prevent XSS by escaping user input in templates.
- Cross-Site Request Forgery (CSRF): Prevent CSRF by using CSRF tokens.
- Authentication and Authorization Issues: Implement secure authentication and authorization mechanisms.
- Denial-of-Service (DoS) Attacks: Implement rate limiting and other measures to prevent DoS attacks.
- Insecure Dependencies: Keep your dependencies up to date to prevent vulnerabilities.
4.2. Input Validation:
- Validate all user input: Validate all user input to prevent malicious data from entering your application.
- Use a validation library: Use a validation library like
marshmalloworvoluptuousto simplify input validation. - Sanitize user input: Sanitize user input to remove potentially harmful characters.
- Limit input length: Limit the length of input fields to prevent buffer overflows.
- Use regular expressions: Use regular expressions to validate input patterns.
4.3. Authentication and Authorization:
- Use a strong authentication scheme: Use a strong authentication scheme like OAuth 2.0 or JWT.
- Store passwords securely: Store passwords securely using a hashing algorithm like bcrypt.
- Implement role-based access control (RBAC): Use RBAC to control access to resources based on user roles.
- Use secure cookies: Use secure cookies to protect session data.
- Implement multi-factor authentication (MFA): Use MFA to add an extra layer of security.
4.4. Data Protection:
- Encrypt sensitive data: Encrypt sensitive data at rest and in transit.
- Use HTTPS: Use HTTPS to encrypt communication between the client and the server.
- Store data securely: Store data in a secure location with appropriate access controls.
- Regularly back up data: Regularly back up data to prevent data loss.
- Comply with data privacy regulations: Comply with data privacy regulations like GDPR and CCPA.
4.5. Secure API Communication:
- Use HTTPS: Always use HTTPS for API communication.
- Implement API authentication: Use API keys or tokens to authenticate API requests.
- Rate limit API requests: Implement rate limiting to prevent abuse.
- Validate API requests: Validate API requests to prevent malicious data from entering your application.
- Log API requests: Log API requests for auditing and debugging.
5. Testing Approaches
5.1. Unit Testing:
- Test individual components: Unit tests should test individual components in isolation.
- Use a testing framework: Use a testing framework like
pytestorunittest. - Write clear and concise tests: Write clear and concise tests that are easy to understand.
- Test edge cases: Test edge cases and boundary conditions.
- Use mocks and stubs: Use mocks and stubs to isolate components under test.
5.2. Integration Testing:
- Test interactions between components: Integration tests should test interactions between different components.
- Test with real dependencies: Integration tests should use real dependencies whenever possible.
- Test the entire application flow: Integration tests should test the entire application flow.
- Use a testing database: Use a testing database to isolate integration tests from the production database.
5.3. End-to-End Testing:
- Test the entire system: End-to-end tests should test the entire system from end to end.
- Use a testing environment: Use a testing environment that mimics the production environment.
- Automate end-to-end tests: Automate end-to-end tests to ensure that the system is working correctly.
- Use a browser automation tool: Use a browser automation tool like Selenium or Puppeteer.
5.4. Test Organization:
- Organize tests by module: Organize tests by module to improve test discovery and maintainability.
- Use descriptive test names: Use descriptive test names that clearly indicate what the test is verifying.
- Use test fixtures: Use test fixtures to set up and tear down test environments.
- Use test markers: Use test markers to categorize tests and run specific test suites.
5.5. Mocking and Stubbing:
- Use mocks to simulate dependencies: Use mocks to simulate the behavior of dependencies.
- Use stubs to provide predefined responses: Use stubs to provide predefined responses to API calls.
- Use mocking libraries: Use mocking libraries like
unittest.mockorpytest-mock. - Avoid over-mocking: Avoid over-mocking, as it can make tests less reliable.
6. Common Pitfalls and Gotchas
6.1. Frequent Mistakes:
- Not handling exceptions properly: Always handle exceptions to prevent unexpected behavior.
- Using blocking operations in asynchronous code: Avoid using blocking operations in asynchronous code.
- Not closing
ClientSession: Always close theClientSessionto release resources. - Not validating user input: Always validate user input to prevent security vulnerabilities.
- Not using HTTPS: Always use HTTPS for secure communication.
6.2. Edge Cases:
- Handling timeouts: Implement proper timeout handling to prevent requests from hanging indefinitely.
- Handling connection errors: Handle connection errors gracefully to prevent application crashes.
- Handling large payloads: Handle large payloads efficiently to prevent memory issues.
- Handling concurrent requests: Handle concurrent requests properly to prevent race conditions.
- Handling Unicode encoding: Be aware of Unicode encoding issues when processing text data.
6.3. Version-Specific Issues:
- aiohttp version compatibility: Be aware of compatibility issues between different aiohttp versions.
- asyncio version compatibility: Be aware of compatibility issues between aiohttp and different asyncio versions.
- Python version compatibility: Be aware of compatibility issues between aiohttp and different Python versions.
6.4. Compatibility Concerns:
- Compatibility with other libraries: Be aware of compatibility issues between aiohttp and other libraries.
- Compatibility with different operating systems: Be aware of compatibility issues between aiohttp and different operating systems.
- Compatibility with different web servers: Be aware of compatibility issues between aiohttp and different web servers.
6.5. Debugging Strategies:
- Use logging: Use logging to track application behavior and identify issues.
- Use a debugger: Use a debugger to step through code and examine variables.
- Use a profiler: Use a profiler to identify performance bottlenecks.
- Use error reporting tools: Use error reporting tools to track and fix errors in production.
- Use a network analyzer: Use a network analyzer like Wireshark to capture and analyze network traffic.
7. Tooling and Environment
7.1. Recommended Development Tools:
- IDE: Use an IDE like VS Code, PyCharm, or Sublime Text.
- Virtual Environment: Use a virtual environment to isolate project dependencies.
- Package Manager: Use a package manager like pip or poetry to manage dependencies.
- Testing Framework: Use a testing framework like pytest or unittest.
- Linting Tool: Use a linting tool like pylint or flake8 to enforce code style.
- Formatting Tool: Use a formatting tool like black or autopep8 to format code automatically.
7.2. Build Configuration:
- Use a build system: Use a build system like Make or tox to automate build tasks.
- Define dependencies in
requirements.txtorpyproject.toml: Specify all project dependencies in arequirements.txtorpyproject.tomlfile. - Use a Dockerfile: Use a Dockerfile to create a containerized build environment.
- Use Docker Compose: Use Docker Compose to manage multi-container applications.
7.3. Linting and Formatting:
- Use a consistent code style: Use a consistent code style throughout the project.
- Configure linting tools: Configure linting tools to enforce code style instructions.
- Configure formatting tools: Configure formatting tools to format code automatically.
- Use pre-commit hooks: Use pre-commit hooks to run linters and formatters before committing code.
7.4. Deployment:
- Use a web server: Use a web server like Nginx or Apache to serve the application.
- Use a process manager: Use a process manager like Supervisor or systemd to manage the application process.
- Use a reverse proxy: Use a reverse proxy to improve security and performance.
- Use a load balancer: Use a load balancer to distribute traffic across multiple servers.
- Use a monitoring system: Use a monitoring system to track application health and performance.
- Standalone Server: aiohttp.web.run_app(), simple but doesn't utilize all CPU cores.
- Nginx + Supervisord: Nginx prevents attacks, allows utilizing all CPU cores, and serves static files faster.
- Nginx + Gunicorn: Gunicorn launches the app as worker processes, simplifying deployment compared to bare Nginx.
7.5. CI/CD Integration:
- Use a CI/CD pipeline: Use a CI/CD pipeline to automate the build, test, and deployment process.
- Use a CI/CD tool: Use a CI/CD tool like Jenkins, GitLab CI, or GitHub Actions.
- Run tests in the CI/CD pipeline: Run tests in the CI/CD pipeline to ensure that code changes don't break the application.
- Automate deployment: Automate deployment to reduce manual effort and improve consistency.
Additional Best Practices:
- Session Management: Always create a
ClientSessionfor making requests and reuse it across multiple requests to benefit from connection pooling. Avoid creating a new session for each request, as this can lead to performance issues. - Error Handling: Implement robust error handling in your request handlers. Use try-except blocks to manage exceptions, particularly for network-related errors. For example, handle
ConnectionResetErrorto manage client disconnections gracefully. - Middleware Usage: Utilize middleware for cross-cutting concerns such as logging, error handling, and modifying requests/responses. Define middleware functions that accept a request and a handler, allowing you to process requests before they reach your main handler.
- Graceful Shutdown: Implement graceful shutdown procedures for your server to ensure that ongoing requests are completed before the application exits. This can be achieved by registering shutdown signals and cleaning up resources.
- Security Practices: When deploying, consider using a reverse proxy like Nginx for added security and performance. Configure SSL/TLS correctly to secure your application.
- Character Set Detection: If a response does not include the charset needed to decode the body, use
ClientSessionaccepts afallback_charset_resolverparameter which can be used to introduce charset guessing functionality. - Persistent Session: Use
Cleanup Contextwhen creating a persistent session.
By adhering to these practices, developers can enhance the reliability, performance, and security of their aiohttp applications.