Skip to content

Custom Resolvers

Platform Users — Engineers & Low-code Ops Users (ORA / Panel Builder) OR Platform ORA — AI Planning Interface Agent Workflows Plan Visualisation ADK Integration SDK UI — Frontend Shell FDK Architecture Low code Config-driven DDK Schema Definition Code Generator Generated Server MDK WEM DAL Experiment Manager Nexus Deployment Control Live Monitoring Registry Browser SCDK Source Control Pipeline Mgmt Azure DevOps deploys ↓ SDK API — GraphQL Federation Gateway Federation Gateway Component Resolvers Auth & Licensing Plugins: gql-autogeneration Migrator Helm KinD Boilerplate GenAI ··· Microservices — Domain IP Services Data Pipeline Core Platform Metrics & Analytics Spatial & Geo Simulation Event Detection Camera & Device Fire & Resource Opt. Satellite Modelling ↓ Nexus deploys Deployed OR Applications Rail Ops Dashboard Mine Mgmt Dashboard Port Ops Dashboard ··· FDK-built · DDK-backed · MDK-powered · deployed via Nexus ↑ Application Users — Operations Teams (shift managers, analysts, planners)

About This Guide

This document helps you extend DDK-generated servers with custom business logic that goes beyond standard CRUD operations. It focuses on how to add specialized queries, mutations, and integrations while preserving your custom code across schema regenerations.

Who this is for: Backend developers and engineers who need to implement business logic, complex workflows, or external integrations on top of DDK-generated servers.

What you'll learn:

  • When and why to use custom resolvers
  • How to define custom operations in your schema
  • How custom code is preserved during regeneration
  • Patterns for authentication, pagination, aggregation, and external integrations
  • Best practices for testing and error handling

What we don't cover: To keep this guide clear and accessible, we don't cover the internal details of the DDK file generation system, the complete generated resolver implementation, or proprietary optimization techniques. For advanced customization scenarios beyond the documented patterns, please contact our support team.


This guide covers how to extend DDK-generated servers with custom business logic beyond standard database operations.

Table of Contents


When to Use Custom Resolvers

The DDK auto-generates standard create, read, update, and delete (CRUD) operations for your database entities. Custom resolvers extend beyond these operations to handle specialized business logic that doesn't fit the standard CRUD pattern.

The DDK's custom resolver system solves a fundamental code generation problem: how to continuously regenerate your database layer while preserving custom business logic you've added. The solution uses file naming conventions—custom resolver files are never overwritten by the generator, while generated files can be safely regenerated at any time.

Complex Business Logic

Custom resolvers enable multi-step workflows, complex validation rules, conditional processing, and business rule enforcement that go beyond simple database operations.

Examples:

  • Order processing workflows with inventory checks and payment validation
  • User registration with email verification and account setup
  • Complex approval workflows with multiple stakeholder checks

Advanced Queries

Handle queries that span multiple database tables, perform aggregations and analytics, or require complex filtering conditions.

Examples:

  • Dashboard analytics combining data from multiple entities
  • Search functionality with relevance scoring
  • Reporting queries with date ranges, filters, and aggregations

External Integrations

Integrate with third-party services, external authentication providers, payment processors, or notification systems.

Examples:

  • Payment gateway integration for transaction processing
  • Email service integration for user notifications
  • SMS messaging for multi-factor authentication
  • Third-party API calls for data enrichment

Computed Fields

Calculate values at query time, derive data from multiple sources, or perform runtime transformations.

Examples:

  • User's full name from first and last name fields
  • Order total from line items
  • Age calculated from birthdate
  • Formatted addresses from component fields

Custom Operations

Implement batch operations, import/export functionality, report generation, or data migration tasks.

Examples:

  • Bulk user import from CSV files
  • PDF report generation
  • Data export to external systems
  • Database migration scripts

Custom Resolver Workflow

Step 1: Define Custom Operations

You define custom operations in your schema by marking them with a special directive that tells the DDK these operations require custom implementation.

Operation Types:

  • Queries — Retrieve data with custom logic (e.g., search, filtered lists, computed data)
  • Mutations — Modify data with business rules (e.g., complex validation, multi-step processes)
  • Subscriptions — Real-time updates over persistent connections (e.g., live notifications, data streams)

Step 2: Generate Scaffolding

When you regenerate your server, the DDK detects your custom operation definitions and generates starter files for implementing them—but only if these files don't already exist. This means:

  • First generation — Creates new scaffolding files with placeholder implementations
  • Subsequent generations — Preserves existing implementations, never overwriting them
  • New operations — Only generates scaffolding for newly-added custom operations

The generated scaffolding includes:

  • Function signatures matching your schema definitions
  • Access to database connections and data repositories
  • Test file templates for unit and integration testing
  • Error handling structure

Step 3: Implement Business Logic

Edit the generated scaffolding files to implement your custom logic. Your implementation has access to:

Database Access:

  • Active database transaction for consistency
  • Typed repositories for querying and modifying entities
  • Query options for filtering, sorting, and pagination

Request Context:

  • User authentication information from JWT tokens
  • Request-specific logging with correlation IDs
  • Session and tenant information

External Services:

  • Caching layer for performance optimization
  • Redis client for distributed state
  • Schema introspection for dynamic queries

Your implementation can:

  • Query multiple database tables and combine results
  • Apply complex validation rules before saving data
  • Call external APIs and integrate responses
  • Calculate derived values and aggregations
  • Trigger asynchronous processes

Step 4: Regenerate Safely

When you add more custom operations:

  1. Update your schema with new operation definitions
  2. Regenerate the server
  3. Existing implementations are preserved — the generator skips files that already exist
  4. New scaffolding files are created only for new operations
  5. Generated standard resolvers are updated if the schema changed

This workflow ensures you can continuously evolve your schema and regeneration without losing custom business logic.


Capabilities

Transaction Management

All custom resolvers operate within database transactions, ensuring data consistency:

Automatic Transaction Handling:

  • Each request begins a new database transaction
  • All queries and mutations execute within this transaction
  • Successful operations commit automatically
  • Errors trigger automatic rollback to maintain consistency
  • Savepoints enable partial rollback of multi-step operations

Benefits:

  • Guaranteed data consistency across multiple database operations
  • No partial writes if an error occurs mid-operation
  • Isolation from concurrent requests
  • Simplified error recovery

Repository Access

Instead of writing raw database queries, custom resolvers use generated type-safe repositories:

Available Operations:

  • Retrieve all records or paginated batches
  • Query by single or multiple field values
  • Filter with custom conditions
  • Retrieve single records by ID or field values
  • Create, update, or delete records
  • Batch operations for efficiency

Query Options:

  • Eager-loading of relationships to avoid N+1 queries
  • Sorting by one or more fields
  • Transaction participation
  • Debug mode for SQL query inspection

Benefits:

  • Type safety prevents field name typos
  • Consistent error handling across operations
  • Automatic SQL injection protection
  • Built-in validation of sort fields and parameters

Error Handling

The DDK provides structured error handling for custom resolvers:

Error Types:

  • Database errors trigger automatic transaction rollback
  • Validation errors return structured messages to clients
  • Business logic errors can provide user-friendly messages
  • System errors are logged with full context for debugging

Error Recovery:

  • Failed operations automatically roll back to savepoints
  • Structured error responses include request correlation IDs
  • Errors are logged with stack traces for investigation
  • Users receive appropriate error messages without system details

Context Values

Custom resolvers have access to request-specific context:

Authentication Information:

  • JWT tokens from request headers
  • Parsed user claims (user ID, roles, permissions)
  • Session identifiers

Request Metadata:

  • Correlation IDs for tracing requests across systems
  • Request-specific loggers with automatic context
  • Timestamp and origin information

System Resources:

  • Active database transaction
  • Redis client for caching
  • Schema introspection capabilities

Relationship Loading

Efficiently load related data to avoid performance issues:

Eager Loading: Load related entities in a single query rather than making separate database calls for each relationship. For example, when retrieving a user, you can load all their posts and comments in one query.

Configurable Loading: Specify which relationships to load based on the request context—load minimal data for list views, comprehensive data for detail views.

Performance Optimization: Avoid N+1 query problems where a list of N items triggers N+1 database queries. Eager loading reduces this to a single query or small number of queries.


Preservation Across Regeneration

The DDK uses multiple mechanisms to preserve custom code when regenerating:

File Naming Convention

Custom resolver files use a distinct naming pattern that marks them as "do not overwrite":

  • Custom files follow a specific naming convention
  • Generator skips any file matching this pattern
  • Only new custom operations generate new files
  • Existing custom implementations are never touched

This convention creates a clear contract: if you write business logic in a custom file, regeneration will never overwrite it.

Generated File Updates

Standard generated resolver files are updated during regeneration:

  • Function signatures are updated if schema changes
  • Existing implementation bodies are preserved where possible
  • New operations are added without affecting existing ones

Bidirectional Synchronization

Before and after regeneration, files are synchronized between working directories to ensure custom implementations are preserved and new generations are captured.


Best Practices

1. Use Transactions

All database operations in custom resolvers should use the provided database transaction:

Why:

  • Ensures consistency with other operations in the request
  • Enables automatic rollback on errors
  • Supports savepoint-based recovery
  • Maintains isolation from concurrent requests

2. Prefer Repository Methods

Use the typed repositories rather than raw database queries when possible:

Benefits:

  • Type-safe field names prevent typos
  • Validated sorting and filtering
  • Consistent error handling
  • Protection against SQL injection
  • Simpler code maintenance

When to use raw queries:

  • Complex joins not well-expressed through repositories
  • Performance-critical queries requiring optimization
  • Database-specific features like full-text search

3. Validate Input Early

Check and validate all input parameters before performing database operations:

Validation Checks:

  • Required fields are present
  • Values are within acceptable ranges
  • Formats match expectations (emails, URLs, dates)
  • Business rule constraints are satisfied

Benefits:

  • Fail fast with clear error messages
  • Avoid partial database writes
  • Reduce transaction time
  • Improve user experience with specific error messages

4. Preload Relationships

Avoid N+1 query problems by loading related data eagerly:

Problem: When retrieving a list of users and their posts, naive implementation makes one query for users, then N separate queries for each user's posts (N+1 total queries).

Solution: Specify relationship preloading to fetch users and all their posts in a single query or small number of optimized queries.

Benefits:

  • Dramatically improved performance
  • Reduced database load
  • Faster response times
  • Predictable query count

5. Document Complex Logic

Add comments explaining the "why" behind non-obvious business rules:

What to document:

  • Business rules and their origins
  • Edge cases and how they're handled
  • External system integration details
  • Performance optimization rationale

What NOT to document:

  • Obvious operations (e.g., "this creates a user")
  • Simple CRUD operations
  • Standard patterns

6. Use Caching

Leverage the Redis client for frequently-accessed data:

Good candidates for caching:

  • Configuration data that changes infrequently
  • User session information
  • Expensive query results
  • API responses from external services

Cache Strategies:

  • Set appropriate expiration times
  • Implement cache invalidation on updates
  • Handle cache misses gracefully
  • Use consistent cache key formats

Common Patterns

Authentication Check

Retrieve and validate user identity from request context:

Use Cases:

  • Operations requiring authenticated users
  • Retrieving current user's data
  • Personalizing responses based on user

Validation:

  • Check authentication claims exist
  • Extract user identifier from claims
  • Verify user exists in database
  • Return appropriate errors for unauthenticated requests

Authorization Check

Verify users have permission to perform operations:

Use Cases:

  • Role-based access control
  • Resource ownership verification
  • Permission-based feature access

Patterns:

  • Check user role from authentication claims
  • Verify resource ownership (e.g., user can only edit their own data)
  • Combine multiple authorization checks (role AND ownership)
  • Return "forbidden" errors when authorization fails

Pagination

Return large result sets in manageable pages:

Benefits:

  • Reduced memory usage
  • Faster response times
  • Better user experience

Parameters:

  • Page number or offset
  • Page size (results per page)
  • Total count for UI pagination controls

Implementation:

  • Calculate offset from page number and size
  • Query with limit and offset
  • Return results with pagination metadata

Aggregation

Calculate summary statistics or grouped results:

Use Cases:

  • Dashboard analytics (total users, average age, count by role)
  • Reporting (sales by month, orders by status)
  • Metrics (active users, conversion rates)

Operations:

  • Count records matching criteria
  • Calculate averages, sums, minimums, maximums
  • Group by specific fields
  • Combine multiple aggregations

Multi-Field Filtering

Query with multiple optional filter criteria:

Implementation:

  • Build filter map dynamically based on provided parameters
  • Handle optional filters (only add if provided)
  • Support array values for "IN" queries
  • Apply additional filtering after repository query if needed

Benefits:

  • Flexible search functionality
  • Single endpoint for multiple filter combinations
  • Type-safe filter application

Testing Approaches

Unit Testing

Test custom resolver logic with mocked dependencies:

What to Mock:

  • Database repositories
  • External API clients
  • Cache/Redis connections
  • Authentication context

What to Test:

  • Business logic correctness
  • Validation rules
  • Error handling
  • Edge cases

Benefits:

  • Fast test execution
  • Isolated testing of business logic
  • No database dependency
  • Deterministic results

Integration Testing

Test resolvers through the API with real database:

What to Test:

  • End-to-end operation flow
  • Database transaction behavior
  • Actual query results
  • API request/response format

Setup:

  • Use test database or embedded database
  • Seed test data
  • Clean up after tests

Benefits:

  • Validates complete integration
  • Catches schema mismatch issues
  • Tests actual database behavior

Manual Testing

Use the built-in GraphQL playground for interactive testing:

How:

  • Access playground at server URL
  • Write queries and mutations manually
  • Inspect results and errors
  • Test edge cases interactively

Benefits:

  • Rapid iteration during development
  • Visual feedback
  • Easy sharing of test cases
  • No test code required

Troubleshooting

Common Issues

Resolver Not Found: Ensure function naming matches generated patterns and schema definitions.

Type Mismatch: Verify return types match schema exactly, including nullable vs non-nullable fields.

Transaction Errors: Always retrieve the transaction from request context, don't create new transactions.

Nil Pointer Errors: Check for optional parameters (nullable types) before using them, and validate context values exist before type assertions.

Relationship Loading Failures: Verify relationship mappings in schema are correct and match database foreign keys.

Query Validation Errors: Repository methods validate field names against schema—ensure field names match exactly.


  • Schema Guide - How to define schemas and custom operations
  • Architecture - Understanding the generated code structure
  • Examples - Real-world schema and resolver examples
  • FAQs - Common questions and answers

User documentation for Optimal Reality