This example demonstrates Mycel's automatic GraphQL query optimization features. No code changes needed - the same HCL configuration produces optimized execution!
When a client requests only specific fields, Mycel automatically rewrites database queries:
# Client request
query {
users {
id
name
}
}-- Without optimization (standard)
SELECT * FROM users
-- With Mycel optimization (automatic!)
SELECT id, name FROM usersBenefits:
- Reduced network bandwidth
- Lower database load
- Less memory usage
When a flow has multiple steps fetching data from external services, Mycel skips steps whose output isn't requested:
# Only base fields - ALL external API calls skipped!
query {
product(id: "prod-1") {
id
name
description
}
}
# Only price requested - only pricing API called
query {
product(id: "prod-1") {
id
name
price
}
}
# All fields - all APIs called
query {
product(id: "prod-1") {
id
name
price # triggers pricing step
stock # triggers inventory step
rating # triggers reviews step
reviewCount # triggers reviews step
}
}When fetching nested data, Mycel batches queries automatically:
query {
orders {
id
user { # Would cause N+1 without DataLoader
name
}
product { # Would cause N+1 without DataLoader
name
}
}
}-- Without DataLoader (N+1 problem) - 10 orders = 21 queries!
SELECT * FROM orders
SELECT * FROM users WHERE id = 'user-1'
SELECT * FROM products WHERE id = 'prod-1'
SELECT * FROM users WHERE id = 'user-2'
SELECT * FROM products WHERE id = 'prod-2'
... (continues for each order)
-- With DataLoader (automatic!) - only 3 queries
SELECT * FROM orders
SELECT * FROM users WHERE id IN ('user-1', 'user-2', 'user-3')
SELECT * FROM products WHERE id IN ('prod-1', 'prod-2', 'prod-3')Use has_field() for explicit control over step execution:
step "pricing" {
when = "has_field(input, 'price') || has_field(input, 'discount')"
connector = "pricing_api"
query = "SELECT * FROM prices WHERE product_id = :id"
}# 1. Navigate to example directory
cd examples/graphql-optimization
# 2. Create and populate database
sqlite3 demo.db < setup.sql
# 3. Start Mycel
mycel start
# 4. Open GraphQL Playground
open http://localhost:4000/playground# Request only 2 fields - database query optimized
query {
users {
id
name
}
}Compare with:
# Request all fields
query {
users {
id
email
name
avatar
bio
createdAt
updatedAt
}
}# Base fields only - NO external API calls
query {
product(id: "prod-1") {
id
name
description
category
}
}# Add price - triggers pricing step ONLY
query {
product(id: "prod-1") {
id
name
price
}
}# Add stock - triggers inventory step
query {
product(id: "prod-1") {
id
name
stock
}
}# Add rating - triggers reviews step
query {
product(id: "prod-1") {
id
name
rating
reviewCount
}
}# Fetches all orders with nested user/product
# Only 3 queries total (not 13!)
query {
orders {
id
total
status
user {
id
name
email
}
product {
id
name
sku
}
}
}| Function | Description | Example |
|---|---|---|
has_field(input, path) |
Check if field was requested | has_field(input, 'price') |
field_requested(input, path) |
Alias for has_field | field_requested(input, 'stock') |
requested_fields(input) |
Get all requested field paths | requested_fields(input) |
requested_top_fields(input) |
Get top-level fields only | requested_top_fields(input) |
When a GraphQL query arrives, Mycel:
- Parses the query AST
- Extracts requested field names
- Stores them in
__requested_fieldsand__requested_top_fields
Before executing a database query:
- Checks if
__requested_top_fieldsis available - Maps GraphQL field names to SQL column names (camelCase → snake_case)
- Rewrites
SELECT *toSELECT specific_columns
Before executing each step:
- Analyzes transform expressions to find which fields use each step
- Compares with
__requested_top_fields - Skips steps whose output isn't needed
For each GraphQL request:
- Creates a new
LoaderCollection - Batches all loads that happen within 1ms window
- Executes single batched query instead of N individual queries
| Scenario | Without Optimization | With Optimization | Improvement |
|---|---|---|---|
| Select 2 of 10 fields | 10 columns fetched | 2 columns fetched | 80% less data |
| Product with 3 external APIs | 4 API calls | 1 API call (if only base fields) | 75% fewer calls |
| 10 orders with nested data | 21 queries | 3 queries | 86% fewer queries |
graphql-optimization/
├── service.hcl # Service configuration
├── connectors.hcl # GraphQL server + databases
├── flows.hcl # Flows with optimization examples
├── types.hcl # GraphQL type definitions
├── setup.sql # Database schema + sample data
└── README.md # This file
-
Check field names: GraphQL uses camelCase, SQL uses snake_case. Mycel converts automatically.
-
Check transform expressions: Step optimizer analyzes
step.Xreferences in transforms. -
Verify __requested_fields: Add a log or inspect input to see what fields were detected.
-
Check timing: Loads within 1ms are batched. Async operations might miss the window.
-
Check context: DataLoader requires the GraphQL context to be passed through.