HyperRoute

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

ArgumentTypeRequiredDefaultDescription
pathString!URL path template. Use {args.fieldName} for argument interpolation
methodString"GET"HTTP method: GET, POST, PUT, DELETE, PATCH
ownerString!Subgraph name that owns this field (must match --name on push)
ttlIntCache 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 --url should 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

ArgumentTypeRequiredDefaultDescription
serviceString!Fully qualified gRPC service name (e.g., package.v1.ServiceName)
methodString!RPC method name (e.g., GetProduct, ListProducts)
ownerString!Subgraph name that owns this field
ttlIntCache 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

FlagDescription
--grpc-service <NAME>Fully qualified gRPC service name (required for gRPC)
--proto-file <PATH>Path to .proto file (optional, for static compilation)
--grpc-reflectionUse gRPC server reflection (default: true)
--grpc-tlsEnable 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