Source:
ocean/docs/adr/ADR-039-graphql-snake-case-standardization.md| ✏️ Edit on GitHub
ADR-039: GraphQL Snake Case Standardization
Status
Accepted
Context
Our GraphQL API had inconsistent field naming conventions:
- Database schema used snake_case (e.g.,
stripe_customer_id) - GraphQL schema mixed camelCase and snake_case
- Resolvers performed manual transformations between naming conventions
- This created unnecessary complexity and potential for errors
Decision
Standardize all GraphQL field names to snake_case to match the database schema.
Changes Made
-
GraphQL Schema Updates:
- All type fields converted to snake_case
- All query and mutation names converted to snake_case
- All input type fields converted to snake_case
- All enum values kept in UPPER_CASE (GraphQL convention)
-
Resolver Updates:
- Removed manual field name transformations
- Updated all resolver function names to snake_case
- Updated argument destructuring to use snake_case
- Return objects now directly match database fields
-
Client Updates:
- GraphQL fragments updated to use snake_case
- Generated TypeScript types regenerated
- ESLint configured to ignore generated files
Consequences
Positive
- Consistency: Single naming convention throughout the stack
- Simplicity: No field name transformations needed
- Performance: Reduced CPU overhead from transformations
- Maintainability: Easier to trace fields from database to API
- Type Safety: Direct mapping between database and GraphQL types
Negative
- Breaking Change: All GraphQL clients must update their queries
- Convention: snake_case in GraphQL is less common than camelCase
- Migration: Existing clients need updates
Migration Path
- Update all client queries to use snake_case fields
- Update any cached query results
- Regenerate client-side GraphQL types
- Test all GraphQL operations
Technical Details
Before
type Organization {
id: ID!
stripeCustomerId: String
createdAt: String!
}
type Query {
myOrganizations: [Organization!]!
organizationMembers(organizationId: ID!): [OrganizationMember!]!
}
After
type Organization {
id: ID!
stripe_customer_id: String
created_at: String!
}
type Query {
my_organizations: [Organization!]!
organization_members(organization_id: ID!): [OrganizationMember!]!
}
Resolver Before
return {
stripeCustomerId: organization.stripe_customer_id,
createdAt: organization.created_at,
}
Resolver After
return {
stripe_customer_id: organization.stripe_customer_id,
created_at: organization.created_at,
}
Related ADRs
- ADR-036: Unified Field Naming Convention
- ADR-037: Comprehensive Code Deduplication
- ADR-038: Edge Function Wrapper Pattern