GraphQL Federation Export
FakeDataDSL can export your schemas to GraphQL Federation 2.0 compatible SDL, perfect for microservices architectures using Apollo Federation, Netflix DGS, or other federated GraphQL implementations.
Quick Start
# Export single schema
fake_data_dsl export schemas/user.dsl -f graphql-federation -o user.graphql
# Export all schemas
fake_data_dsl export schemas/ --all -f graphql-federation -o schema.graphql
# Ruby API
schema = FakeDataDSL.load("schemas/user.dsl")
graphql = FakeDataDSL::Export::GraphQLFederation.new(schema).export
puts graphql
Basic Usage
Single Schema Export
# Input schema (user.dsl)
# User:
# id: uuid
# name: name
# email: email
# role: enum(user, admin, moderator)
# active: boolean
# created_at: timestamp
schema = FakeDataDSL.load("schemas/user.dsl")
exporter = FakeDataDSL::Export::GraphQLFederation.new(schema)
puts exporter.export
Output:
extend schema
@link(url: "https://specs.apollo.dev/federation/v2.0")
type User @key(fields: "id") {
id: ID!
name: String!
email: String!
role: UserRole!
active: Boolean!
created_at: DateTime!
}
enum UserRole {
USER
ADMIN
MODERATOR
}
extend type Query {
user(id: ID!): User
users(first: Int, after: String): UserConnection!
}
type UserConnection {
edges: [UserEdge!]!
pageInfo: PageInfo!
}
type UserEdge {
node: User!
cursor: String!
}
type PageInfo {
hasNextPage: Boolean!
hasPreviousPage: Boolean!
startCursor: String
endCursor: String
}
Multiple Schemas
# Export multiple schemas as one federated graph
exporter = FakeDataDSL::Export::GraphQLFederation.new(
FakeDataDSL.load("schemas/user.dsl"),
FakeDataDSL.load("schemas/order.dsl"),
FakeDataDSL.load("schemas/product.dsl")
)
puts exporter.export
Federation Directives
@key Directive
Entity keys are automatically inferred from id fields or fields marked with @key:
# In DSL
User:
id: uuid @key
email: email @key # Additional key
# Output
type User @key(fields: "id") @key(fields: "email") {
id: ID!
email: String!
}
@external and @provides
For cross-service references:
# In DSL
Order:
id: uuid
user_id: Ref(User.id) # Reference to User service
items: array(OrderItem, 1..10)
total: money
# Configure federation
FakeDataDSL::Export::GraphQLFederation.configure do |config|
config.external_types = ["User"]
config.provides = {
"Order" => { "user" => "id name email" }
}
end
Output:
type Order @key(fields: "id") {
id: ID!
user: User! @provides(fields: "id name email")
items: [OrderItem!]!
total: Float!
}
type User @key(fields: "id", resolvable: false) @external {
id: ID!
}
@shareable
Mark types as shareable across services:
# In DSL
Address:
@shareable
street: text
city: city
country: country
Output:
type Address @shareable {
street: String!
city: String!
country: String!
}
Subgraph Configuration
Service Definition
exporter = FakeDataDSL::Export::GraphQLFederation.new(schema,
service_name: "users-service",
service_url: "https://users.example.com/graphql",
version: "2.0"
)
Multiple Subgraphs
Generate separate files for each subgraph:
# Define subgraph boundaries
subgraphs = {
"users" => ["User", "Profile"],
"orders" => ["Order", "OrderItem"],
"products" => ["Product", "Category"]
}
subgraphs.each do |name, types|
schemas = types.map { |t| FakeDataDSL.schema(t) }
exporter = FakeDataDSL::Export::GraphQLFederation.new(*schemas,
service_name: "#{name}-service"
)
File.write("subgraphs/#{name}.graphql", exporter.export)
end
Type Mapping
DSL to GraphQL Types
| DSL Type | GraphQL Type |
|---|---|
uuid |
ID! |
text, name, email |
String! |
number |
Int! |
float, money |
Float! |
boolean |
Boolean! |
date |
Date! |
timestamp |
DateTime! |
enum(...) |
Custom Enum |
array(...) |
[Type!]! |
optional (?) |
Nullable |
json |
JSON (custom scalar) |
Custom Scalars
FakeDataDSL::Export::GraphQLFederation.configure do |config|
config.custom_scalars = {
"date" => "Date",
"timestamp" => "DateTime",
"json" => "JSON",
"money" => "BigDecimal",
"url" => "URL",
"email" => "Email"
}
end
Output includes scalar definitions:
scalar Date
scalar DateTime
scalar JSON
scalar BigDecimal
scalar URL
scalar Email
Mutations
Auto-Generated Mutations
exporter = FakeDataDSL::Export::GraphQLFederation.new(schema,
generate_mutations: true
)
Output:
extend type Mutation {
createUser(input: CreateUserInput!): User!
updateUser(id: ID!, input: UpdateUserInput!): User!
deleteUser(id: ID!): Boolean!
}
input CreateUserInput {
name: String!
email: String!
role: UserRole
active: Boolean
}
input UpdateUserInput {
name: String
email: String
role: UserRole
active: Boolean
}
Custom Mutations
# In DSL with @mutation annotation
User:
id: uuid
name: name
email: email
@mutations
create: CreateUser(name!, email!, role?)
update: UpdateUser(id!, name?, email?, role?)
deactivate: DeactivateUser(id!, reason?)
Subscriptions
exporter = FakeDataDSL::Export::GraphQLFederation.new(schema,
generate_subscriptions: true
)
Output:
extend type Subscription {
userCreated: User!
userUpdated(id: ID): User!
userDeleted: ID!
}
Entity Resolution
_entities Query
Federation requires an _entities query for entity resolution:
exporter = FakeDataDSL::Export::GraphQLFederation.new(schema,
include_entities_query: true # default: true
)
Output:
union _Entity = User | Order | Product
type Query {
_entities(representations: [_Any!]!): [_Entity]!
_service: _Service!
}
type _Service {
sdl: String!
}
scalar _Any
Interfaces and Unions
Interfaces
# In DSL
Node:
@interface
id: uuid
User < Node:
name: name
email: email
Product < Node:
title: text
price: money
Output:
interface Node {
id: ID!
}
type User implements Node @key(fields: "id") {
id: ID!
name: String!
email: String!
}
type Product implements Node @key(fields: "id") {
id: ID!
title: String!
price: Float!
}
Unions
# In DSL
SearchResult:
@union User | Product | Order
Output:
union SearchResult = User | Product | Order
extend type Query {
search(query: String!): [SearchResult!]!
}
Pagination
Relay-Style Connections
exporter = FakeDataDSL::Export::GraphQLFederation.new(schema,
pagination_style: :relay # default
)
Output:
type UserConnection {
edges: [UserEdge!]!
pageInfo: PageInfo!
totalCount: Int!
}
type UserEdge {
node: User!
cursor: String!
}
Offset Pagination
exporter = FakeDataDSL::Export::GraphQLFederation.new(schema,
pagination_style: :offset
)
Output:
type UserList {
items: [User!]!
total: Int!
offset: Int!
limit: Int!
}
extend type Query {
users(offset: Int, limit: Int): UserList!
}
CLI Options
fake_data_dsl export [SCHEMA_PATH] -f graphql-federation [OPTIONS]
Options:
-o, --output PATH Output file path
--service-name NAME Service name for subgraph
--service-url URL Service URL for federation
--federation-version VER Federation version (1 or 2, default: 2)
--no-mutations Don't generate mutations
--no-subscriptions Don't generate subscriptions
--no-pagination Don't generate connection types
--pagination-style STYLE Pagination style (relay or offset)
--external-types TYPES Comma-separated external type names
Examples:
# Basic export
fake_data_dsl export schemas/ -f graphql-federation -o schema.graphql
# With service configuration
fake_data_dsl export schemas/user.dsl -f graphql-federation \
--service-name users \
--service-url https://users.api.example.com/graphql \
-o users.graphql
# Minimal export (no mutations/subscriptions)
fake_data_dsl export schemas/ -f graphql-federation \
--no-mutations --no-subscriptions \
-o types-only.graphql
API Reference
GraphQLFederation.new
FakeDataDSL::Export::GraphQLFederation.new(*schemas, = {})
Parameters:
schemas- One or more Schema objectsoptions[:service_name]- Subgraph service nameoptions[:service_url]- Subgraph service URLoptions[:version]- Federation version ("1" or "2")options[:generate_mutations]- Include mutations (default: false)options[:generate_subscriptions]- Include subscriptions (default: false)options[:pagination_style]- :relay or :offsetoptions[:include_entities_query]- Include _entities (default: true)options[:external_types]- Array of external type namesoptions[:custom_scalars]- Hash of DSL type => GraphQL scalar
GraphQLFederation#export
exporter.export
# => String containing complete GraphQL SDL
GraphQLFederation#export_to_file
exporter.export_to_file("schema.graphql")
Integration Examples
Apollo Router
# supergraph.yaml
federation_version: 2
subgraphs:
users:
routing_url: https://users.example.com/graphql
schema:
file: ./users.graphql # Generated by FakeDataDSL
orders:
routing_url: https://orders.example.com/graphql
schema:
file: ./orders.graphql
Netflix DGS
// The generated schema works with DGS
@DgsComponent
class UserDataFetcher {
@DgsQuery
fun user(@InputArgument id: String): User {
// Resolver implementation
}
@DgsEntityFetcher(name = "User")
fun user(values: Map<String, Any>): User {
// Entity resolver for federation
}
}
Apollo Server
// The generated schema works with Apollo Server
import { buildSubgraphSchema } from '@apollo/subgraph';
import { readFileSync } from 'fs';
const typeDefs = gql(readFileSync('./user.graphql', 'utf8'));
const server = new ApolloServer({
schema: buildSubgraphSchema({ typeDefs, resolvers })
});
Best Practices
1. One Entity Per Service
# Good: Clear ownership
# users-service owns User, Profile
# orders-service owns Order, OrderItem
# Avoid: Shared ownership
# Both services define User (unless @shareable)
2. Use @key Thoughtfully
# Good: Natural keys
User:
id: uuid @key
email: email @key # Secondary lookup
# Avoid: Composite keys when unnecessary
User:
id: uuid @key
tenant_id: uuid @key # Only if truly needed
3. Keep Schemas Aligned
# Use the DSL as source of truth
# Generate GraphQL from DSL, not the other way around
Troubleshooting
Missing @key Directive
# Make sure id field exists or add @key annotation
User:
id: uuid # Auto-detected as key
# Or explicit
User:
user_id: uuid @key # Explicit key
Enum Name Conflicts
# Use namespaced enums
User:
status: enum(active, inactive) @enum_name(UserStatus)
Product:
status: enum(draft, published) @enum_name(ProductStatus)
Circular References
# Federation handles this via external types
# User service:
User:
orders: array(Ref(Order))
# Order service:
Order:
user: Ref(User) @external
See Also
- GraphQL Export - Standard GraphQL (non-federated)
- OpenAPI Export
- Protobuf Export
- Type Reference