Routing Directives
Routing directives tell the HyperRoute router how to reach non-GraphQL backends. They are declared in your subgraph schema and preserved through composition into the supergraph.
@rest Directive
Routes GraphQL fields to REST/HTTP endpoints.
Declaration
directive @rest(
path: String!
method: String = "GET"
owner: String!
ttl: Int
) on FIELD_DEFINITION
Arguments
| Argument | Type | Required | Default | Description |
|---|---|---|---|---|
path | String! | ✅ | — | URL path template. Use {args.fieldName} for argument interpolation |
method | String | ❌ | "GET" | HTTP method: GET, POST, PUT, DELETE, PATCH |
owner | String! | ✅ | — | Subgraph name that owns this field (must match --name on push) |
ttl | Int | ❌ | — | Cache TTL in milliseconds |
Path Templates
Path templates support argument interpolation using {args.<name>} syntax:
# Path parameter
user(id: ID!): User @rest(path: "/users/{args.id}", owner: "users-api")
# Query parameters
users(limit: Int, offset: Int): [User!]!
@rest(path: "/users?limit={args.limit}&offset={args.offset}", owner: "users-api")
# Nested path parameters
userOrders(userId: ID!): [Order!]!
@rest(path: "/users/{args.userId}/orders", owner: "users-api")
Full Example
directive @rest(
path: String!
method: String = "GET"
owner: String!
ttl: Int
) on FIELD_DEFINITION
type User @key(fields: "id") {
id: ID!
name: String!
email: String!
createdAt: DateTime
}
type Query {
user(id: ID!): User
@rest(path: "/users/{args.id}", owner: "users-api")
users(limit: Int, offset: Int): [User!]!
@rest(path: "/users?limit={args.limit}&offset={args.offset}", owner: "users-api")
}
type Mutation {
createUser(input: CreateUserInput!): User
@rest(path: "/users", method: "POST", owner: "users-api")
updateUser(id: ID!, input: UpdateUserInput!): User
@rest(path: "/users/{args.id}", method: "PUT", owner: "users-api")
deleteUser(id: ID!): Boolean
@rest(path: "/users/{args.id}", method: "DELETE", owner: "users-api")
}
Push Command
hyperroute push --local \
--name users-api \
--schema users-api.graphql \
--url http://localhost:4001 \
--subgraph-type rest
Note: For REST subgraphs, the
--urlshould be the base URL of the REST service (without/graphql).
@grpc Directive
Routes GraphQL fields to gRPC service methods.
Declaration
directive @grpc(
service: String!
method: String!
owner: String!
ttl: Int
) on FIELD_DEFINITION
Arguments
| Argument | Type | Required | Default | Description |
|---|---|---|---|---|
service | String! | ✅ | — | Fully qualified gRPC service name (e.g., package.v1.ServiceName) |
method | String! | ✅ | — | RPC method name (e.g., GetProduct, ListProducts) |
owner | String! | ✅ | — | Subgraph name that owns this field |
ttl | Int | ❌ | — | Cache TTL in milliseconds |
Full Example
directive @grpc(
service: String!
method: String!
owner: String!
ttl: Int
) on FIELD_DEFINITION
type Product @key(fields: "id") {
id: ID!
name: String!
description: String!
price: Float!
stock: Int!
category: String!
}
type ProductList {
products: [Product!]!
nextPageToken: String
totalCount: Int!
}
type Query {
product(id: ID!): Product
@grpc(service: "inventory.v1.ProductService", method: "GetProduct", owner: "inventory-grpc")
products(pageSize: Int, category: String): ProductList
@grpc(service: "inventory.v1.ProductService", method: "ListProducts", owner: "inventory-grpc")
searchProducts(query: String!, pageSize: Int): ProductList
@grpc(service: "inventory.v1.ProductService", method: "SearchProducts", owner: "inventory-grpc")
}
type Mutation {
createProduct(input: CreateProductInput!): Product
@grpc(service: "inventory.v1.ProductService", method: "CreateProduct", owner: "inventory-grpc")
updateStock(productId: ID!, delta: Int!): Product
@grpc(service: "inventory.v1.ProductService", method: "UpdateStock", owner: "inventory-grpc")
}
Push Command
hyperroute push --local \
--name inventory-grpc \
--schema inventory-grpc.graphql \
--url grpc://localhost:50051 \
--subgraph-type grpc \
--grpc-service inventory.v1.ProductService
gRPC-Specific Options
| Flag | Description |
|---|---|
--grpc-service <NAME> | Fully qualified gRPC service name (required for gRPC) |
--proto-file <PATH> | Path to .proto file (optional, for static compilation) |
--grpc-reflection | Use gRPC server reflection (default: true) |
--grpc-tls | Enable TLS for gRPC connection |
Mixing Subgraph Types
You can compose GraphQL, REST, and gRPC subgraphs into a single supergraph:
# Push all three types
hyperroute push --local --name accounts-gql \
--schema ./gql-service/schema.gql \
--url http://localhost:3006/graphql
hyperroute push --local --name users-api \
--schema ./users-api.graphql \
--url http://localhost:4001 \
--subgraph-type rest
hyperroute push --local --name inventory-grpc \
--schema ./inventory-grpc.graphql \
--url grpc://localhost:50051 \
--subgraph-type grpc \
--grpc-service inventory.v1.ProductService
The resulting subgraphs.json will contain:
{
"version": "2",
"subgraphs": {
"accounts-gql": {
"url": "http://localhost:3006/graphql"
},
"users-api": {
"url": "http://localhost:4001",
"type": "rest"
},
"inventory-grpc": {
"url": "grpc://localhost:50051",
"type": "grpc",
"grpc": {
"service": "inventory.v1.ProductService",
"use_reflection": true
}
}
}
}
Directive Reference Card
@rest
directive @rest(
path: String! # URL path template with {args.x} interpolation
method: String # HTTP method (default: "GET")
owner: String! # Subgraph name
ttl: Int # Cache TTL in ms
) on FIELD_DEFINITION
@grpc
directive @grpc(
service: String! # Fully qualified gRPC service name
method: String! # RPC method name
owner: String! # Subgraph name
ttl: Int # Cache TTL in ms
) on FIELD_DEFINITION