DEV Community

Cover image for GraphQL Federation with Ballerina and Apollo - Part I
Thisaru Guruge
Thisaru Guruge

Posted on • Updated on • Originally published at thisaru.me

GraphQL Federation with Ballerina and Apollo - Part I

This article was written using Ballerina Swan Lake Update 8 (2201.8.0)

Introduction

GraphQL: Striking a Balance Between Advantages and Limitations

GraphQL, a relatively recent addition to the API world, is in a constant process of evolution. Like any innovation, GraphQL possesses strengths and limitations that the dynamic GraphQL community aggressively addresses. Within GraphQL APIs, a key benefit is the accessibility of data from a single endpoint. Clients request exactly what they need, and the server responds with exactly the requested information. While this presents an appealing prospect for front-end developers, the same feature can turn into a nightmare for back-end developers. Imagine a GraphQL API with hundreds of types, each with multiple fields. Managing such an API would become an almost insurmountable challenge.

Introducing GraphQL Federation: Unifying Complex APIs

Enter GraphQL Federation - an architectural paradigm. GraphQL Federation resolves the aforementioned issue by segmenting a singular GraphQL API into smaller components, known as subgraphs. These subgraphs, manageable and scalable, can be differentiated based on the separation of concerns. Various teams can then oversee different subgraphs, with GraphQL Federation seamlessly integrating them into a unified GraphQL API. This ensures a seamless experience for clients, harmonizing the intricacies behind the scenes, while making life easy for the back-end developers.
This article series will dive into GraphQL federation with Ballerina and Apollo Studio.

How Does GraphQL Federation Work?

GraphQL Federation consists of 4 major components.

  • Subgraph(s)
  • Supergraph Schema
  • GraphQL Router
  • Schema Registry

Federated Subgraph

A federated subgraph functions as an independent GraphQL API, typically centered around a specific domain. Typically, a subgraph is overseen by a single team, with distinct implementation details and design preferences, encompassing choices related to data sources, programming languages, and deployment strategies. A federated supergraph, in turn, incorporates one or more of these subgraphs.

Note: It's worth highlighting that GraphQL Federation permits the creation of a federated GraphQL service with a single subgraph. This approach offers scalability for greenfield projects, allowing seamless expansion. For brownfield projects, it facilitates a transition from a monolithic GraphQL API to a Federated GraphQL API.

Supergraph Schema

The supergraph schema is a GraphQL schema that encompasses all the subgraphs within the federation. Constructing the supergraph schema entails the composition of all individual subgraphs. A tool like Rover CLI possesses the capability to seamlessly combine these subgraphs into a supergraph schema.

Note: In a later section of this article series, you will delve into the intricacies of the supergraph composition process.

GraphQL Router

The GraphQL router is the magical mediator between the GraphQL client and the subgraphs. True to its name, the router efficiently directs GraphQL requests from clients to their respective subgraphs. Particularly in scenarios involving numerous subgraphs with complex relationships, the router is smart enough to devise a query plan to gather necessary data from each subgraph by referring to the supergraph schema. Once the data is collected from these subgraphs, the router stitches them together, generating a singular response dispatched back to the client. This seamless orchestration ensures that the client's request is fulfilled precisely, all the while being blissfully unaware that the data has been seamlessly curated from multiple GraphQL APIs (subgraphs).

Schema Registry

In a federated architecture, the schema registry assumes the role of a version control system for the schema. This registry diligently records alterations made to the federated supergraph schema, ensuring efficient management and conflict reduction while safeguarding the integrity of the supergraph schema.

What You'll Build

You will build a product review system using Ballerina and Apollo Federation in this example.
This service should provide the functionality to retrieve product information, user information, and review information. Additionally, it should provide the functionality to add new reviews to the products.
Although you are going to implement a federated GraphQL API, first you are going to define the GraphQL schema disregarding the federation aspect. Once the API is defined, it can be broken down into smaller subgraphs.

Gathering Requirements

To define our GraphQL API effectively, it's crucial to start by gathering the requirements. The best way to do this is by articulating the use cases. Below, we present a set of simple, sample use cases that will guide the creation of our GraphQL API.

Use Case 1 - Retrieve Product Information

The first use case addresses the need for clients to retrieve a list of all products available. This is the GraphQL query for that purpose:

query GetAllProducts {
  products {
    id
    name
    reviews {
      rating
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

Additionally, we must cater to the requirement of retrieving a specific product by its unique ID. This is the corresponding GraphQL query:

query GetProduct($id: ID!) {
  product(id: $id) {
    id
    name
    description
    price
    reviews {
      title
      rating
      comment
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

Use Case 2 - Retrieve User Information

In the second use case, clients need to fetch a list of all users within the system. The GraphQL query for this purpose is as follows:

query GetUsers {
  users {
    id
    name
    email
  }
}
Enter fullscreen mode Exit fullscreen mode

Use Case 3 - Retrieve Review Information

This scenario involves retrieving product review information, filtered either by user ID or product ID. Here are the GraphQL queries for these situations:
For reviews filtered by user ID:

query GetReviews($userId: ID) {
  reviews(userId: $userId) {
    title
    comment
    rating
  }
}
Enter fullscreen mode Exit fullscreen mode

For reviews filtered by product ID:

query GetReviews($productId: ID) {
  reviews(productId: $productId) {
    title
    comment
    rating
    author {
      id
      name
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

Use Case 4 - Add Review

The final use case entails adding a new review to a product. This is achieved through a mutation operation, and the GraphQL mutation looks like this:

mutation AddReview($input: ReviewInput!) {
  addReview(reviewInput: $input): Review!
}
Enter fullscreen mode Exit fullscreen mode

Where following is a sample ReviewInput:

{
  "input": {
    "title": "Nice Product",
    "comment": "Value for money",
    "rating": 5
  }
}
Enter fullscreen mode Exit fullscreen mode

Designing the API

Now that we have a clear understanding of the requirements through these use cases, we can proceed to define the schema for our GraphQL API. Building upon these use cases, we can derive the following GraphQL schema:

type Query {
  products: [Product!]!
  product(id: ID!): Product
  users: [User!]!
  reviews(productId: ID, authorId: ID): [Review!]!
}

type Mutation {
  addReview(reviewInput: ReviewInput!): Review
}

type Product {
  id: ID!
  name: String!
  price: Float!
  description: String!
  reviews: [Review!]!
}

type Review {
  id: ID!
  title: String!
  comment: String!
  rating: Int!
  product: Product!
  author: User!
}

type User {
  id: ID!
  name: String!
  email: String!
  reviews: [Review!]!
}

input ReviewInput {
  title: String!
  comment: String!
  productId: ID!
}
Enter fullscreen mode Exit fullscreen mode

With our GraphQL API schema defined based on the gathered requirements, we can now proceed to the next step.

Designing the Federated Supergraph

To transform our GraphQL schema into a federated supergraph, we employ a separation of concerns approach. The schema can be logically divided into three main areas: Products, Users, and Reviews. Each of these areas can be implemented as a separate subgraph, often managed by different teams.
It's important to note that in a federated GraphQL API, there may be types that span multiple subgraphs. These types are referred to as Entity types in GraphQL federation. To identify Entity types in our schema, we can annotate the schema with subgraph affiliations as comments.

Note: These comments are for illustrative purposes only. They are not part of the GraphQL schema and they will not affect the schema in any way.

type Query {
  products: [Product!]! # Products Subgraph
  product(id: ID!): Product # Products Subgraph
  users: [User!]! # Users Subgraph
  reviews(productId: ID, authorId: ID): [Review!]! # Reviews Subgraph
}

type Mutation {
  addReview(reviewInput: ReviewInput!): Review # Reviews Subgraph
}

type Product {
  id: ID! # Products Subgraph
  name: String! # Products Subgraph
  price: Float! # Products Subgraph
  description: String! # Products Subgraph
  reviews: [Review!]! # Reviews Subgraph
}

type Review {
  id: ID! # Reviews Subgraph
  title: String! # Reviews Subgraph
  comment: String! # Reviews Subgraph
  rating: Int! # Reviews Subgraph
  product: Product! # Reviews Subgraph
  author: User! # Reviews Subgraph
}

type User {
  id: ID! # Users Subgraph
  name: String! # Users Subgraph
  email: String! # Users Subgraph
  reviews: [Review!]! # Reviews Subgraph
}

input ReviewInput {
  title: String! # Reviews Subgraph
  comment: String! # Reviews Subgraph
  productId: ID! # Reviews Subgraph
}
Enter fullscreen mode Exit fullscreen mode

This annotation practice helps us understand the schema's design and the affiliation of types with different subgraphs.

In our schema, we have identified two Entity types:

  • Product - This type spans both the Products Subgraph and the Reviews Subgraph.
  • User - Similarly, the User type spans both the Users Subgraph and the Reviews Subgraph.

Now, with a well-structured GraphQL schema in place, we are ready to proceed to the next step of designing the federated supergraph.

Implementing Federated Subgraphs in Ballerina

In this section, we'll walk through the process of implementing individual subgraphs using Ballerina. Each subgraph corresponds to a specific domain within our federated GraphQL API. We'll create three subgraphs: Products Subgraph, Users Subgraph, and Reviews Subgraph.

Prerequisites

Before diving into the code, it's worth noting that this section provides a high-level overview of the subgraph implementations. Detailed code explanations are omitted to maintain focus on the key concepts of GraphQL federation. For in-depth Ballerina GraphQL implementation, refer to the provided resources.

Resources for Ballerina GraphQL Implementation

For a comprehensive understanding of Ballerina GraphQL implementation, explore these resources:

Now, let's proceed with creating the subgraphs without the federation-specific features. We'll add federation-specific functionality later in this article series.

Products Subgraph

Schema for the Products Subgraph

We begin by defining the GraphQL schema for the Products Subgraph, which includes two query fields.

type Query {
  products: [Product!]!
  product(id: ID!): Product
}

type Product {
  id: ID!
  name: String!
  description: String!
  price: Float!
}
Enter fullscreen mode Exit fullscreen mode

To create the Products Subgraph project, use the following Ballerina command:

bal new products_subgraph -t service
Enter fullscreen mode Exit fullscreen mode

This command initializes a new Ballerina project under the products_subgraph directory.

Data Source for the Products Subgraph

Most GraphQL services have their own data sources. In this example, we use an in-memory Ballerina table as the data source, but this can be easily replaced with a database. To make the data source replaceable, we implement it as a separate Ballerina module.

To add the data source module, navigate to the root directory of the Products subgraph repository and execute this command:

bal add datasource
Enter fullscreen mode Exit fullscreen mode

This command creates a new datasource directory under the modules directory. Inside this directory, add the following code:

Types Used in Products Data Source

Define a single type, Product, used in the data source:

type Product readonly & record {|
  string id;
  string name;
  string description;
  float price;
|};
Enter fullscreen mode Exit fullscreen mode
Products Data Source API

Implement the data source API, which provides access to the product data:

public isolated function getProducts() returns readonly & Product[] {
  lock {
    return from Product product in products select product;
  }
}

public isolated function getProduct(string id) returns Product? {
  lock {
    return products[id];
  }
}
Enter fullscreen mode Exit fullscreen mode
Sample Product Data

Define a sample data set for the Product type:

isolated table<Product> key(id) products = table [
  {id: "product_0", name: "Shoes", description: "A pair of shoes", price: 100},
  {id: "product_1", name: "T-shirt", description: "A t-shirt", price: 10},
  {id: "product_2", name: "Pants", description: "A pair of pants", price: 50}
];
Enter fullscreen mode Exit fullscreen mode

GraphQL Service for Products Subgraph

Now, let's create the main GraphQL service for the Products Subgraph. Below is the initial code for this subgraph service:

import product_subgraph.datasource;

import ballerina/graphql;

service graphql:Service on new graphql:Listener(9091) {
  resource function get products() returns Product[] => datasource:getProducts();

  resource function get product(@graphql:ID string id) returns Product? =>
  datasource:getProduct(id);
}
Enter fullscreen mode Exit fullscreen mode
Types of the Products Subgraph

The next step is to define the types required for the GraphQL service of the Products Subgraph. In this case, we have a single type, Product:

import ballerina/graphql;

public type Product record {|
  @graphql:ID string id;
  string name;
  string description;
  float price;
|};
Enter fullscreen mode Exit fullscreen mode

For the detailed implementation of this subgraph, refer to the Products Subgraph repository.

Users Subgraph

The Users Subgraph follows a similar structure as the Products Subgraph. Here, we'll define the schema, data source, GraphQL service, and types for the Users Subgraph.

Schema for the Users Subgraph

Define the GraphQL schema for the Users Subgraph, which includes a single query field:

type Query {
  users: [User!]!
}

type User {
  id: ID!
  name: String!
  email: String!
}
Enter fullscreen mode Exit fullscreen mode

To create the Users Subgraph project, follow the same steps as the Products Subgraph and execute this command:

bal new users_subgraph -t service
Enter fullscreen mode Exit fullscreen mode

Data Source for the Users Subgraph

Create a data source module for the Users Subgraph, similar to the Products Subgraph, with types, data source API, and sample user data.

Types Used in Users Data Source

Define the User type for the data source:

public type User record {|
  string id;
  string name;
  string email;
|};
Enter fullscreen mode Exit fullscreen mode

Users Data Source API

Implement the data source API for accessing user data:

public isolated function getUsers() returns readonly & User[] {
  lock {
    return from User user in users select user;
  }
}
Enter fullscreen mode Exit fullscreen mode

Sample User Data

Define a sample data set for the User type:

isolated table<User> key(id) users = table [
  {id: "user_0", name: "Alice", email: "alice@sample.com"},
  {id: "user_1", name: "Bob", email: "bob@sample.com"},
  {id: "user_2", name: "Charlie", email: "charlie@sample.com"},
  {id: "user_3", name: "Dave", email: "dave@sample.com"},
  {id: "user_4", name: "Eve", email: "eve@sample.com"}
];
Enter fullscreen mode Exit fullscreen mode

GraphQL Service for Users Subgraph

Create the GraphQL service for the Users Subgraph:

import user_subgraph.datasource;

import ballerina/graphql;

service graphql:Service on new graphql:Listener(9092) {
  resource function get users() returns User[] => datasource:getUsers();
}
Enter fullscreen mode Exit fullscreen mode

Types of the Users Subgraph

Define the GraphQL types for the Users Subgraph, including the User type:

import ballerina/graphql;

public type User record {|
  @graphql:ID string id;
  string name;
  string email;
|};
Enter fullscreen mode Exit fullscreen mode

The Users Subgraph implementation details can be found in the Users Subgraph repository.

Reviews Subgraph

The Reviews Subgraph follows a similar structure as the previous subgraphs. Here, we'll define the schema, data source, GraphQL service, and types for the Reviews Subgraph.

Schema for the Reviews Subgraph

Define the GraphQL schema for the Reviews Subgraph, which includes queries and a mutation for adding reviews:

type Query {
  reviews: [Review!]!
}

type Review {
  id: ID!
  title: String!
  comment: String!
  rating: Int!
}

type Mutation {
  addReview(input: ReviewInput!): Review!
}

input ReviewInput {
  title: String!
  comment: String!
  rating: Int!
  authorId: String!
  productId: String!
}
Enter fullscreen mode Exit fullscreen mode

Note: The product and the author fields of the Review type are not added here. Those fields will be added in a later section of this article series.

To create the Reviews Subgraph project, follow the same steps as the previous subgraphs and execute this command:

bal new reviews_subgraph -t service
Enter fullscreen mode Exit fullscreen mode

Data Source for the Reviews Subgraph

Create a data source module for the Reviews Subgraph, similar to the previous subgraphs, with types, data source API, and sample review data.

Types Used in Reviews Data Source

Define two types of the data source: Review and ReviewInput:

type Review record {|
  readonly string id;
  string title;
  string comment;
  int rating;
  string authorId;
  string productId;
|};

type ReviewInput record {|
  string title;
  string comment;
  int rating;
  string authorId;
  string productId;
|};
Enter fullscreen mode Exit fullscreen mode

Reviews Data Source API

Implement the data source API for accessing review data and adding new reviews:

public isolated function getReviews() returns readonly & Review[] {
  lock {
    return from Review review in reviews select review;
  }
}

public isolated function addReview(ReviewInput input) returns Review {
  lock {
    string nextId = string `review-${reviews.length()}`;
    Review review = {
      id: nextId,
      ...input
    };
    reviews.add(review);
    return review;
  }
}
Enter fullscreen mode Exit fullscreen mode

Sample Review Data

Define a sample data set for the Review type:

isolated table<Review> key(id) reviews = table [
  {id: "review_0", title: "Great!", comment: "Value for money", rating: 4, productId: "product_1", authorId: "user_1"},
  {id: "review_1", title: "Good", comment: "Fast delivery", rating: 3, productId: "product_1", authorId: "user_2"},
  {id: "review_2", title: "Bad", comment: "Not as described", rating: 1, productId: "product_2", authorId: "user_3"},
  {id: "review_3", title: "Great!", comment: "Value for money", rating: 4, productId: "product_2", authorId: "user_1"}
];
Enter fullscreen mode Exit fullscreen mode

GraphQL Service for Reviews Subgraph

Create the GraphQL service for the Reviews Subgraph, including query and mutation operations:

import review_subgraph.datasource;

import ballerina/graphql;

service graphql:Service on new graphql:Listener(9093) {
  resource function get reviews() returns Review[] {
    return from ReviewInfo reviewInfo in datasource:getReviews()
    select new (reviewInfo);
  }

  remote function addReview(ReviewInput input) returns Review {
    ReviewInfo result = datasource:addReview(input);
    return new (result);
  }
}
Enter fullscreen mode Exit fullscreen mode

Types of the Reviews Subgraph

Define the GraphQL types for the Reviews Subgraph, including the Review type and ReviewInput input type:

import ballerina/constraint;
import ballerina/graphql;

public isolated service class Review {
  private final readonly & ReviewInfo reviewInfo;

  isolated function init(ReviewInfo reviewInformation) {
    self.reviewInfo = reviewInformation.cloneReadOnly();
  }

  isolated resource function get id() returns @graphql:ID string => self.reviewInfo.id;
  isolated resource function get title() returns string => self.reviewInfo.title;
  isolated resource function get comment() returns string => self.reviewInfo.comment;
  isolated resource function get rating() returns int => self.reviewInfo.rating;
}

type ReviewInfo record {|
  string id;
  string title;
  string comment;
  int rating;
  string authorId;
  string productId;
|};

public type ReviewInput readonly & record {|
  string title;
  string comment;
  @constraint:Int {
    minValue: 0,
    maxValue: 5
  }
  int rating;
  string authorId;
  string productId;
|};
Enter fullscreen mode Exit fullscreen mode

Note: The Review type is defined using a Ballerina service type here. Although it could be defined using a record type, the service type is chosen because certain federation-specific features (which will be discussed later in this article series) require this type to be defined as a service type.

For a detailed implementation of this subgraph, refer to the Reviews Subgraph repository.

With these subgraphs implemented, you have laid the foundation for your federated GraphQL API. In the next sections, we will enhance these subgraphs with federation-specific features to build the supergraph.

Publish Subgraphs

Now that we have our initial implementation complete, we can publish them. To publish the subgraphs we can use the Rover CLI from Apollo.

Publishing to Apollo Studio

To publish the subgraphs to Apollo Studio, first, create an account on Apollo Studio and create a new Graph. Refer to Apollo documentation for more information about creating new graphs and publishing them. When you create a new Graph, a pop-up shows up with an APOLLO_KEY, and APOLLO_GRAPH_REF. Copy these values and save them in a .env file.

APOLLO_KEY=<Your Apollo Key>
APOLLO_GRAPH_REF=<Your Apollo Graph Ref>
Enter fullscreen mode Exit fullscreen mode

First, we need to configure the Rover CLI to connect with our Graph in the Apollo Studio. Execute the following command to configure the Rover CLI.

rover config auth
Enter fullscreen mode Exit fullscreen mode

This will prompt you to enter the Apollo key. Copy and paste the APOLLO_KEY obtained above here.

Note: Make sure to copy and paste the complete key, which starts from service:.

If the key is applied successfully, the following message will be printed on the stdout:

Successfully saved API key.
Enter fullscreen mode Exit fullscreen mode

Once the key is successfully set, we can publish the subgraph.

To publish the subgraph we need the GraphQL schema of the subgraph. To get the subgraph, we can use the bal graphql CLI tool. Execute the following command inside the Products subgraph repo:

bal graphql -i service.bal
Enter fullscreen mode Exit fullscreen mode

This will generate the GraphQL schema for the subgraph and save it in a file named schema_service.graphql. Once the schema is created, you can publish the subgraph to Apollo Studio. For testing purposes, we can use the locally running subgraph for this. Execute the following command in the root of the Products subgraph repository:

bal run
Enter fullscreen mode Exit fullscreen mode

This will run the GraphQL service locally. Now that we have a running instance of the subgraph, we can publish it to the Apollo studio using Rover CLI. You need the APOLLO_GRAPH_REF obtained above for this step. Execute the following command to publish the subgraph:

rover subgraph publish <APOLLO_GRAPH_REF> \
-name Products \
-schema ./schema_service.graphql \
-routing-url http://localhost:9091
Enter fullscreen mode Exit fullscreen mode

Repeat the same steps for the other two subgraphs to publish them to Apollo Studio.

Note: You do not need to configure the Rover CLI every time. When publishing just change the subgraph name, schema file, and the url accordingly.

Once all the subgraphs are published, we can visit the Apollo Studio and check our schema. Following is a sample GraphQL API schema generated in the Apollo Studio:

type Mutation {
  addReview(input: ReviewInput!): Review!
}

type Product {
  id: ID!
  name: String!
  description: String!
  price: Float!
}

type Query {
  products: [Product!]!
  product(id: ID!): Product
  reviews: [Review!]!
  users: [User!]!
}

type Review {
  id: ID!
  title: String!
  comment: String!
  rating: Int!
}

input ReviewInput {
  title: String!
  comment: String!
  rating: Int!
  authorId: String!
  productId: String!
}

type User {
  id: ID!
  name: String!
  email: String!
}
Enter fullscreen mode Exit fullscreen mode

In addition to this schema, there will be another schema generated from the Apollo Studio, which is our supergraph schema. Following is the supergraph schema generated from the subgraphs we published above:

schema
  @link(url: "https://specs.apollo.dev/link/v1.0")
  @link(url: "https://specs.apollo.dev/join/v0.3", for: EXECUTION)
{
  query: Query
  mutation: Mutation
}

directive @join__enumValue(graph: join__Graph!) repeatable on ENUM_VALUE

directive @join__field(graph: join__Graph, requires: join__FieldSet, provides: join__FieldSet, type: String, external: Boolean, override: String, usedOverridden: Boolean) repeatable on FIELD_DEFINITION | INPUT_FIELD_DEFINITION

directive @join__graph(name: String!, url: String!) on ENUM_VALUE

directive @join__implements(graph: join__Graph!, interface: String!) repeatable on OBJECT | INTERFACE

directive @join__type(graph: join__Graph!, key: join__FieldSet, extension: Boolean! = false, resolvable: Boolean! = true, isInterfaceObject: Boolean! = false) repeatable on OBJECT | INTERFACE | UNION | ENUM | INPUT_OBJECT | SCALAR

directive @join__unionMember(graph: join__Graph!, member: String!) repeatable on UNION

directive @link(url: String, as: String, for: link__Purpose, import: [link__Import]) repeatable on SCHEMA

scalar join__FieldSet

enum join__Graph {
  PRODUCTS @join__graph(name: "Products", url: "http://localhost:9091")
  REVIEWS @join__graph(name: "Reviews", url: "http://localhost:9093")
  USERS @join__graph(name: "Users", url: "http://localhost:9092")
}

scalar link__Import

enum link__Purpose {
  """
  `SECURITY` features provide metadata necessary to securely resolve fields.
  """
  SECURITY
  """
  `EXECUTION` features provide metadata necessary for operation execution.
  """
  EXECUTION
}

type Mutation
  @join__type(graph: REVIEWS)
{
  addReview(input: ReviewInput!): Review!
}

type Product
  @join__type(graph: PRODUCTS)
{
  id: ID!
  name: String!
  description: String!
  price: Float!
}

type Query
  @join__type(graph: PRODUCTS)
  @join__type(graph: REVIEWS)
  @join__type(graph: USERS)
{
  products: [Product!]! @join__field(graph: PRODUCTS)
  product(id: ID!): Product @join__field(graph: PRODUCTS)
  reviews: [Review!]! @join__field(graph: REVIEWS)
  users: [User!]! @join__field(graph: USERS)
}

type Review
  @join__type(graph: REVIEWS)
{
  id: ID!
  title: String!
  comment: String!
  rating: Int!
}

input ReviewInput
  @join__type(graph: REVIEWS)
{
  title: String!
  comment: String!
  rating: Int!
  authorId: String!
  productId: String!
}

type User
  @join__type(graph: USERS)
{
  id: ID!
  name: String!
  email: String!
}
Enter fullscreen mode Exit fullscreen mode

As you can see, the Apollo Studio already has composed a supergraph schema from the subgraphs you published. Now that we have the supergraph ready, we can use the Apollo Router as the access point of our supergraph.

Note: You can compose your supergraph schema locally using the Rover CLI. Refer to Apollo documentation for more information.

Configure Apollo Router

Create a new directory and download the Apollo Router.

Note: This article series shows how to implement federated GraphQL API using a self-hosted Apollo router. To use the self-hosted Apollo router, you need an Apollo enterprise plan.

Then create a separate file named router.yaml file. This is used to configure the Apollo Router. Following is a sample configuration file:

sandbox:
  enabled: true

homepage:
  enabled: false

supergraph:
  introspection: true

include_subgraph_errors:
  all: true # Propagate errors from all subgraphs

cors:
  origins:
    - http://localhost:4000
    - https://studio.apollographql.com
Enter fullscreen mode Exit fullscreen mode

Refer to the Apollo Router documentation for more information about configuring the router.

Now we can run the router. You need the APOLLO_KEY and the APOLLO_GRAPH_REF. Execute the following command to run the Apollo Router:

APOLLO_KEY=<Your Apollo Key> APOLLO_GRAPH_REF=<Your Apollo Graph Ref> ./router - config router.yaml
Enter fullscreen mode Exit fullscreen mode

Once the router is up and running, it will print the router link on the stdout (the default value is http://localhost:4000), so that you can try out your federated supergraph. When you access the link using a browser, it will start an Apollo Sandbox instance. (This is configured using the router.yaml file mentioned above).

Now use the following GraphQL document to test your federated GraphQL API:

query ExampleQuery {
  products {
    id
    name
  }
  reviews {
    id
    rating
  }
  users {
    id
    name
  }
}
Enter fullscreen mode Exit fullscreen mode

This will return the following response if everything is running correctly.

{
  "data": {
    "products": [
      {
        "id": "product_0",
        "name": "Shoes"
      },
      {
        "id": "product_1",
        "name": "T-shirt"
      },
      {
        "id": "product_2",
        "name": "Pants"
      }
    ],
    "reviews": [
      {
        "id": "review_0",
        "rating": 4
      },
      {
        "id": "review_1",
        "rating": 3
      },
      {
        "id": "review_2",
        "rating": 1
      },
      {
        "id": "review_3",
        "rating": 4
      }
    ],
    "users": [
      {
        "id": "1",
        "name": "Alice"
      },
      {
        "id": "2",
        "name": "Bob"
      },
      {
        "id": "3",
        "name": "Charlie"
      },
      {
        "id": "4",
        "name": "Dave"
      },
      {
        "id": "5",
        "name": "Eve"
      }
    ]
  }
}
Enter fullscreen mode Exit fullscreen mode

Congratulations! Now you have a working federated supergraph. Let's check how to add Entity types to this federated supergraph in the next part of this article series.

Continue reading on Part II of this article series.

Ballerina is an open-source project. We welcome any kind of contributions to the Ballerina platform, including starring on GitHub.

Top comments (0)