214 lines
10 KiB
Plaintext
214 lines
10 KiB
Plaintext
---
|
|
description: This rule provides comprehensive best practices and coding standards for developing with Apollo GraphQL. It covers schema design, security, performance, testing, and deployment.
|
|
globs: *.js,*.jsx,*.ts,*.tsx,*.graphql
|
|
---
|
|
|
|
# Apollo GraphQL Best Practices
|
|
|
|
This document provides a comprehensive guide to best practices and coding standards for developing GraphQL APIs and applications using the Apollo ecosystem.
|
|
|
|
## Library Information:
|
|
- Name: apollo-graphql
|
|
- Tags: web, api, graphql, javascript
|
|
|
|
## 1. Schema Design
|
|
|
|
- **Clarity and Consistency:**
|
|
- Use descriptive naming conventions for types, fields, and arguments (e.g., `getUserById` instead of `getUser`).
|
|
- Maintain consistency across the schema in terms of naming and structure.
|
|
- Define a clear schema that reflects your business domain and data model.
|
|
|
|
- **Interfaces and Unions:**
|
|
- Implement interfaces and unions for shared features and common data structures.
|
|
- Use interfaces to define contracts for types that implement shared behaviors.
|
|
- Use unions when a field can return different object types that don't share a common interface.
|
|
|
|
- **Nullability:**
|
|
- Every field is nullable by default unless explicitly marked as non-null using `!`. Carefully consider nullability for each field.
|
|
- Use non-null types (`!`) only when you can guarantee that the field will always return a value.
|
|
- Handle potential errors gracefully and return `null` for nullable fields when appropriate.
|
|
|
|
- **Demand-Driven Schema Design:**
|
|
- Design your schema to serve client use cases and product requirements.
|
|
- Intentionally design your schema to serve client use cases and product requirements.
|
|
|
|
- **Avoid Autogenerated Schemas:**
|
|
- Avoid autogenerating schemas, especially the fields on the root operation types
|
|
|
|
## 2. Security
|
|
|
|
- **Disable Introspection in Production:**
|
|
- Disable introspection in production environments to prevent unauthorized access to your schema.
|
|
- Restrict access to staging environments where introspection is enabled.
|
|
|
|
- **Input Validation:**
|
|
- Validate all user inputs to prevent injection attacks and data corruption.
|
|
- Use custom scalars or directives to enforce validation rules on input types.
|
|
- Sanitize and escape user inputs before using them in resolvers.
|
|
|
|
- **Authentication and Authorization:**
|
|
- Implement robust authentication and authorization mechanisms to protect your API.
|
|
- Use industry-standard protocols like OAuth 2.0 or JWT for authentication.
|
|
- Implement fine-grained authorization checks at the field level.
|
|
- Enforcing authentication and authorization in the router protects your underlying APIs from malicious operations
|
|
|
|
- **Rate Limiting and Depth Limiting:**
|
|
- Implement rate limiting to prevent abuse and denial-of-service attacks.
|
|
- Limit query depth to prevent complex queries from overwhelming your server.
|
|
|
|
- **Whitelisting Queries:**
|
|
- Consider whitelisting queries to restrict the operations that can be executed against your API.
|
|
|
|
- **Obfuscate Error Details:**
|
|
- Remove verbose error details from API responses in your production graph.
|
|
- Only selectively expose error details to clients in production.
|
|
|
|
- **Data Validation and Sanitization:**
|
|
- As a schema design best practice, you should deliberately design your schema to serve client use cases and product requirements.
|
|
|
|
## 3. Performance Optimization
|
|
|
|
- **Batching and Caching:**
|
|
- Utilize batching techniques (e.g., DataLoader) to reduce the number of requests to backend data sources.
|
|
- Implement caching at different levels (e.g., server-side, client-side) to improve response times.
|
|
|
|
- **Pagination:**
|
|
- Implement pagination strategies to manage large datasets effectively.
|
|
- Use cursor-based pagination for efficient retrieval of paginated data.
|
|
|
|
- **N+1 Problem:**
|
|
- Use tools like DataLoader to address the N+1 query problem and ensure efficient data fetching.
|
|
|
|
- **Server-Side Batching & Caching:**
|
|
- GraphQL is designed in a way that allows you to write clean code on the server, where every field on every type has a focused single-purpose function for resolving that value.
|
|
|
|
- **Automatic Persisted Queries (APQ):**
|
|
- Consider implementing automatic persisted queries (APQ) to optimize network usage and improve security
|
|
|
|
## 4. Code Organization and Structure
|
|
|
|
- **Directory Structure:**
|
|
|
|
src/
|
|
schema/
|
|
types/
|
|
*.graphql
|
|
resolvers/
|
|
*.js
|
|
dataSources/
|
|
*.js
|
|
utils/
|
|
*.js
|
|
index.js // Entry point
|
|
|
|
|
|
- **File Naming Conventions:**
|
|
- Use PascalCase for type definitions (e.g., `UserType.graphql`).
|
|
- Use camelCase for resolver functions (e.g., `getUserById.js`).
|
|
|
|
- **Module Organization:**
|
|
- Organize your code into reusable modules based on functionality (e.g., user management, product catalog).
|
|
- Create separate modules for schema definitions, resolvers, and data sources.
|
|
|
|
- **Component Architecture:**
|
|
- Follow a component-based architecture for building GraphQL applications.
|
|
- Create reusable components for common UI elements and data fetching logic.
|
|
|
|
- **Code Splitting:**
|
|
- Use code splitting to reduce the initial bundle size and improve page load times.
|
|
- Consider splitting your application into smaller chunks based on routes or features.
|
|
|
|
## 5. Common Patterns and Anti-patterns
|
|
|
|
- **Design Patterns:**
|
|
- **Data Source Pattern:** Decouple data fetching logic from resolvers using data sources.
|
|
- **Schema Stitching:** Combine multiple GraphQL APIs into a single, unified schema.
|
|
|
|
- **Recommended Approaches:**
|
|
- Use a GraphQL client library (e.g., Apollo Client, Relay) for efficient data fetching and caching.
|
|
- Implement custom directives to add additional functionality to your schema.
|
|
|
|
- **Anti-patterns:**
|
|
- **Over-fetching/Under-fetching:** Avoid returning more or less data than required by the client.
|
|
- **Chatty APIs:** Reduce the number of round trips between the client and the server.
|
|
- **God Objects:** Avoid creating large, monolithic types with too many fields.
|
|
|
|
- **State Management:**
|
|
- Use a state management library (e.g., Redux, Zustand, Jotai) to manage client-side state.
|
|
- Consider using Apollo Client's local state management features for simple state requirements.
|
|
|
|
- **Error Handling:**
|
|
- Use a consistent error handling mechanism across your application.
|
|
- Return informative error messages to the client.
|
|
- Log errors on the server for debugging purposes.
|
|
|
|
## 6. Testing Approaches
|
|
|
|
- **Unit Testing:**
|
|
- Unit test individual resolvers and data sources.
|
|
- Mock external dependencies to isolate the code under test.
|
|
|
|
- **Integration Testing:**
|
|
- Integrate test your GraphQL API with your database and other backend services.
|
|
- Use a testing framework like Jest or Mocha for writing integration tests.
|
|
|
|
- **End-to-End Testing:**
|
|
- Use end-to-end testing to verify the entire application flow.
|
|
- Use a testing tool like Cypress or Puppeteer for writing end-to-end tests.
|
|
|
|
- **Test Organization:**
|
|
- Organize your tests into separate directories based on functionality.
|
|
- Use descriptive names for your test files and test cases.
|
|
|
|
- **Mocking and Stubbing:**
|
|
- Use mocking and stubbing techniques to isolate the code under test and simulate external dependencies.
|
|
|
|
## 7. Common Pitfalls and Gotchas
|
|
|
|
- **N+1 Problem:** Be aware of the N+1 query problem and use DataLoader or other batching techniques to solve it.
|
|
- **Schema Evolution:** Plan for schema evolution and use techniques like adding new fields and deprecating old ones to avoid breaking changes.
|
|
- **Performance Bottlenecks:** Monitor your API for performance bottlenecks and use profiling tools to identify slow resolvers.
|
|
- **Nullability:** Ensure that non-null fields never return `null` to avoid unexpected errors.
|
|
|
|
## 8. Tooling and Environment
|
|
|
|
- **Development Tools:**
|
|
- Use a GraphQL IDE like GraphiQL or Apollo Studio for exploring and testing your API.
|
|
- Use code generation tools to generate types and resolvers from your schema.
|
|
|
|
- **Build Configuration:**
|
|
- Use a build tool like Webpack or Parcel to bundle your code for production.
|
|
- Configure your build tool to optimize your code and reduce bundle size.
|
|
|
|
- **Linting and Formatting:**
|
|
- Use a linter like ESLint or Prettier to enforce code style and prevent errors.
|
|
|
|
- **Deployment:**
|
|
- Deploy your GraphQL API to a production environment like AWS Lambda, Google Cloud Functions, or a Node.js server.
|
|
- Use a serverless platform for easy scaling and management.
|
|
|
|
- **CI/CD Integration:**
|
|
- Integrate your GraphQL API with a CI/CD pipeline for automated testing and deployment.
|
|
|
|
## 9. Additional Best Practices
|
|
|
|
- **Versioning:** While GraphQL promotes continuous evolution, consider versioning your API if you need to make breaking changes.
|
|
- **Documentation:** Provide comprehensive documentation for your GraphQL API using tools like GraphQL Docs.
|
|
- **Monitoring:** Monitor your GraphQL API for performance and errors using tools like Apollo Studio or New Relic.
|
|
- **Error Messages and Notifications:** You can also opt for union types to represent an error and to prompt suggestions to users, though this is a more expensive choice.
|
|
|
|
## 10. Global Identification
|
|
|
|
- Another way to organize components, besides Pagination, is by using a global identification. Originally proposed on Relay and similar to URIs, this method has become a more general good practice though it is not considered mandatory — especially if you are not planning on supporting Relay in your application
|
|
|
|
## 11. Rate Limiting
|
|
|
|
- Like any other web API, setting limits is a good strategy to avoid, for example, an overload of requests per minute. There are a few ways this can be done in GraphQL
|
|
|
|
## 12. Authentication and Authorization
|
|
|
|
- Often interchanged in their meaning, authentication is the act of determining who a user is and whether they are logged in or not. Authorization, on the other hand, is the act of determining if a user is allowed to do an action or see something.
|
|
|
|
## 13. Safelisting with Persisted queries
|
|
|
|
- Beyond operation limits, GraphOS enables first-party apps to register trusted operations in a persisted query list ( PQL) or safelist. |