Appearance
FAQs
Frequently asked questions about the Data Development Kit (DDK).
Table of Contents
- General Questions
- Schema Design
- Generated Code
- Custom Resolvers
- Database Operations
- Performance and Optimization
- Deployment
- Troubleshooting
General Questions
What is the DDK?
The DDK (Data Development Kit) is a code generation service that creates complete GraphQL servers in Go from schema definitions. It generates a fully functional API with database integration, Docker configuration, and complete server infrastructure.
How does the DDK differ from gqlgen?
- gqlgen: Low-level code generation library, requires manual configuration
- DDK: High-level service that uses gqlgen internally and adds automatic CRUD operations, database integration with GORM and PostgreSQL, custom directive system, complete server scaffolding, Docker setup, and relationship management.
When should I use the DDK?
Use the DDK when you need rapid GraphQL API development with automatic CRUD operations, PostgreSQL integration, relationship management between entities, built-in authentication, and consistent code structure across your services.
The DDK is the right choice for teams building data services that will evolve over time — where the schema will change, new types will be added, and the ability to regenerate without losing custom code is essential. If you need a GraphQL API that you write once and never regenerate, you could use the DDK for initial scaffolding and then treat the output as a starting point. But the DDK's real value is in the ongoing development cycle: edit schema, regenerate, continue. Teams that want instant, zero-code database APIs backed by an existing database should look at Hasura or PostGraphile. Teams that need a generated starting point they can build sophisticated domain logic on top of — while retaining full control of the generated code — should use the DDK.
What is generated vs what is manual?
Generated (automatic):
- GraphQL schema files
- CRUD resolvers for all operations
- Database models and repositories
- GraphQL type definitions
- Server configuration
- Docker setup
Manual (your code):
- Custom resolver implementations
- Business logic
- Custom validation
- External integrations
Schema Design
Do I have to use all CRUD operations?
No! Use the @required directive to specify which operations you need. You can choose any combination of CREATE, READ, UPDATE, and DELETE operations for each type.
Can I use custom scalars?
Yes! The DDK supports custom scalars including Time (for timestamps), JSON (for flexible data structures), and Upload (for file uploads).
How do I model optional vs required fields?
Use GraphQL's type system with the exclamation mark (!) to denote required fields. Fields without the exclamation mark are optional. In the database, required fields become NOT NULL columns while optional fields become nullable columns.
Can I have multiple many-to-many relationships between the same types?
Not directly. Use intermediate types to model each relationship separately. This provides better clarity and control over the relationship data.
How do I create a unique composite constraint?
Use a join table with composite primary key by applying the @constraint(type: "primarykey") directive to multiple fields. Both fields together form the primary key, ensuring uniqueness.
Can I use check constraints with multiple columns?
Yes, check constraints can reference multiple columns. Note that SQL column names use snake_case, so GraphQL field names in camelCase are converted appropriately in constraints.
Generated Code
Can I modify generated code?
DO NOT modify:
- Generated resolver files - Regenerated every time
- Generated schema files - Recreated from your schema definitions
- Generated configuration files - Regenerated with each build
SAFE to modify:
- Custom resolver files (marked with custom naming convention)
- Server entry point (after initial generation)
- Custom service implementations
- Environment configuration
What happens to my custom resolvers when I regenerate?
They are preserved! The DDK uses file naming conventions to distinguish between generated and custom code. Custom resolver files are never overwritten during regeneration, allowing you to safely update your schema and regenerate without losing business logic.
How do I add custom middleware?
Edit the middleware directory after generation and register your custom middleware in the server entry point.
Can I change the GraphQL endpoint?
Yes, edit the server entry point to modify the GraphQL endpoint path.
How do I add custom types that aren't in the schema?
Create custom type definitions in the models directory and use them in your custom resolvers.
Custom Resolvers
Do custom resolvers require database transactions?
Not required, but highly recommended for consistency across operations, rollback on errors, and proper connection pooling.
Can I call other generated resolvers from custom resolvers?
Yes! Access repository methods to leverage generated CRUD operations within your custom business logic.
How do I return errors from custom resolvers?
Return standard errors directly. The GraphQL layer automatically converts them to GraphQL error responses.
Can I use external APIs in custom resolvers?
Yes, custom resolvers can call external services, APIs, or any other integration you need for your business logic.
How do I implement subscriptions?
Use channels to implement real-time subscriptions. The GraphQL server supports WebSocket-based subscription handling.
Database Operations
Does the DDK support database migrations?
Auto-migration (development):
- Automatically creates and updates tables during development
- Does not drop columns or tables
Manual migrations (production):
- Disable auto-migration in production
- Use migration tools for better control over schema changes
- Recommended for production deployments
Can I use composite primary keys?
Yes, apply the primary key constraint to multiple fields to create a composite primary key.
How do I handle soft deletes?
Add a timestamp field for tracking deletions and implement custom delete logic that updates the timestamp instead of removing the record.
Can I use different PostgreSQL schemas?
Yes, configure the database schema name in the server configuration. All tables will be created in the specified schema.
How do I handle connection pooling?
The ORM handles connection pooling automatically. You can configure pool settings such as maximum idle connections, maximum open connections, and connection lifetime.
Performance and Optimization
How do I optimize queries with many relationships?
Use GraphQL field selection to load only the relationships your query needs. The generated resolvers use lazy loading, so unrequested relationships aren't fetched.
Can I add database indexes?
Yes, but requires manual migration. Create database indexes on frequently queried columns to improve query performance.
How do I implement pagination?
Generated list operations have built-in pagination with limit, offset, sorting by field, and sort order parameters. For custom resolvers, implement pagination manually using similar parameters.
Should I use Redis caching?
Use Redis for:
- Session storage
- Authentication token management
- Frequently accessed data
- Rate limiting
- Pub/sub for subscriptions
Redis integration is available through server configuration.
How do I batch queries to avoid N+1 problems?
Implement custom resolvers with batching using dataloader libraries to efficiently load related entities in batches rather than one at a time.
Deployment
What's the recommended production setup?
- Application: Docker container
- Database: Managed PostgreSQL service
- Redis: Managed Redis service
- Orchestration: Kubernetes or container orchestration platform
- Load Balancer: Standard load balancing solution
- Secrets: Secure secrets management service
- Monitoring: Logging and monitoring system
How do I deploy to Kubernetes?
Create Kubernetes manifests for deployment configuration, services, secrets, and ingress. The generated server includes Docker configuration that works with standard Kubernetes deployment patterns.
How do I handle database migrations in production?
Option 1: Init container that runs migrations before application starts
Option 2: CI/CD pipeline step that runs migrations during deployment
Option 3: Separate migration job that runs before deploying new versions
How do I configure environment variables?
Use environment files or environment configuration. The server reads configuration from environment variables for database connection, server settings, Redis configuration, authentication secrets, and logging levels.
How do I enable HTTPS?
Option 1: Use a reverse proxy (recommended) with SSL termination
Option 2: Configure the server directly with SSL certificates
Troubleshooting
"Invalid schema" error when validating
Common causes:
- Missing primary key on a type
- Invalid relationship with foreign key referencing non-existent type
- Syntax error in constraint definition
- Missing required directive
Solution: Run schema validation and read error messages carefully to identify the specific issue.
"Port already in use" error
Solution 1: Change the server port in configuration
Solution 2: Stop the process using the port
Generated resolvers not working after regeneration
Check:
- Did you run regeneration (not initial creation)?
- Are schema files in the correct location?
- Did the code generation run successfully? (check logs)
- Did database schema change? (run migration if needed)
Solution: Delete generated files and regenerate from clean state.
Custom resolvers not called
Check:
- Is the custom resolver directive present in the schema?
- Is the file named correctly as a custom resolver?
- Is the file in the correct directory?
- Did you regenerate after adding the custom operation?
Database connection failed
Check:
- PostgreSQL is running
- Credentials are correct
- Database exists
- Host and port are correct
- Firewall allows connection
Memory leaks in subscriptions
Cause: Not closing channels when client disconnects
Solution: Always check for context cancellation and close channels properly when subscriptions end.
Slow query performance
Solutions:
- Add database indexes on frequently queried columns
- Use eager loading for relationships to avoid N+1 queries
- Implement pagination for large result sets
- Use Redis caching for frequently accessed data
- Optimize constraints and complex queries
Type assertion errors in custom resolvers
Always check if context values exist before asserting their type to avoid panics from nil values.
Getting Help
Where can I find more examples?
- Examples Documentation - Real-world schemas
- Generated code - Review what the DDK creates
How do I report bugs?
Contact your internal development team or check your organization's issue tracking system.
Where can I learn more about GraphQL?
Where can I learn more about GORM?
Related Documentation
- Schema Guide - Writing schemas
- Custom Resolvers - Adding custom logic
- Architecture - How the DDK works
- Usage - Using the DDK service
- Examples - Real-world examples
