Skip to content
Santekno.com | Tech Tutorials and Trends
EN
📖 0%
05 Sep 2025 · 6 min read ·Article 67 / 125
Go

67 Connecting Multiple GraphQL Services in Go

IH
Ihsan Arif
Writer at Santekno · Backend Engineer

67 Connecting Multiple GraphQL Services in Go

As a microservices system grows in scale, the need to integrate several services usually arises so that you can deliver a consistent API experience. One increasingly popular approach is GraphQL federation, where several services, each with their own GraphQL API, are combined into a single unified gateway. In this article, I’ll walk through how to connect multiple GraphQL services in Go, complete with code examples, request simulations, and flow diagrams for building a simple federation.


Why GraphQL Federation?

Before diving into the implementation, let’s first understand the why. Here are a few reasons behind the popularity of GraphQL federation:

BenefitExplanation
API ConsistencyConsumers only need to connect to a single GraphQL endpoint.
Independent DevelopmentEach team can build and release its GraphQL schema independently.
ScalabilityServices can be split apart and scaled on their own.
Single Source of TruthThe gateway can unify data from various sources into one large graph.

The GraphQL Library Landscape in Go

At the time of writing, the most popular GraphQL libraries in Go include:

When it comes to federation, the Go ecosystem isn’t yet as mature as Node.js. Even so, with a few tricks and a lightweight orchestrator, a federation solution can still be built. The approach we’ll use here is the Gateway BFF Pattern.


Case Study: Combining user-service and post-service

Imagine we have two services:

  • user-service: serves user data.
  • post-service: serves post data, with a reference to a user.

We want the gateway to expose a single GraphQL endpoint that can access the combined data, for example: retrieving a list of posts along with their authors’ names.

1. Architecture Structure

MERMAID
flowchart TD
  subgraph Backend
    US(User Service) 
    PS(Post Service)
  end
  GQ[GraphQL Gateway] --> US
  GQ --> PS
  CLIENTS((Clients)) --> GQ

1. Creating the Microservice Schemas

Let’s start by defining the schema for each service.

user-service/schema.graphqls

graphql
 1type User {
 2  id: ID!
 3  name: String!
 4  email: String!
 5}
 6
 7type Query {
 8  user(id: ID!): User
 9  users: [User!]!
10}

post-service/schema.graphqls

graphql
 1type Post {
 2  id: ID!
 3  title: String!
 4  content: String!
 5  authorID: ID!
 6}
 7
 8type Query {
 9  post(id: ID!): Post
10  posts: [Post!]!
11}

2. Building the GraphQL Service in Go (gqlgen)

Here’s a simple implementation example for user-service (only the essential file, kept concise for clarity):

user-service/main.go

go
 1package main
 2
 3import (
 4    "log"
 5    "net/http"
 6    "github.com/99designs/gqlgen/graphql/handler"
 7    "github.com/99designs/gqlgen/graphql/playground"
 8    "user-service/graph"
 9    "user-service/graph/generated"
10)
11
12func main() {
13    srv := handler.NewDefaultServer(generated.NewExecutableSchema(generated.Config{Resolvers: &graph.Resolver{}}))
14
15    http.Handle("/", playground.Handler("GraphQL playground", "/query"))
16    http.Handle("/query", srv)
17
18    log.Println("connect to http://localhost:8081/ for user service playground")
19    log.Fatal(http.ListenAndServe(":8081", nil))
20}

The resolver implementation can use simple in-memory data, and the same applies to post-service.


3. Building the GraphQL Gateway (API Orchestrator)

This is the heart of the article: connecting two (or more) GraphQL services into a single orchestrator. In Go, we don’t yet have full Apollo Federation support, so we’ll use Schema Stitching in a BFF style.

Gateway Schema

We want users to be able to run a query like the following:

graphql
 1{
 2  posts {
 3    id
 4    title
 5    author {
 6      id
 7      name
 8    }
 9  }
10}

This means we need to add a “author” virtual field to the Post graph in the gateway, whose resolver makes a remote call to user-service.

Gateway Code Example

Installing the supporting packages:

text
1go get github.com/99designs/gqlgen
2go get github.com/machinebox/graphql

gateway/schema.graphqls

graphql
 1type User {
 2  id: ID!
 3  name: String!
 4  email: String!
 5}
 6
 7type Post {
 8  id: ID!
 9  title: String!
10  content: String!
11  author: User!
12}
13
14type Query {
15  posts: [Post!]!
16  post(id: ID!): Post
17}

gateway/resolver.go (the core of the stitch pattern)

go
 1package graph
 2
 3import (
 4  "context"
 5  "github.com/machinebox/graphql"
 6)
 7
 8type Resolver struct{}
 9
10type Post struct {
11  ID      string
12  Title   string
13  Content string
14  AuthorID string
15}
16
17func (r *queryResolver) Posts(ctx context.Context) ([]*Post, error) {
18  // call post-service
19  client := graphql.NewClient("http://localhost:8082/query")
20  req := graphql.NewRequest(`
21    query { posts { id title content authorID } }
22  `)
23  var resp struct {
24    Posts []*Post
25  }
26  if err := client.Run(ctx, req, &resp); err != nil {
27    return nil, err
28  }
29  return resp.Posts, nil
30}
31
32func (r *postResolver) Author(ctx context.Context, obj *Post) (*User, error) {
33  // call user-service
34  client := graphql.NewClient("http://localhost:8081/query")
35  req := graphql.NewRequest(`
36    query ($id: ID!) { user(id: $id) { id name email } }
37  `)
38  req.Var("id", obj.AuthorID)
39  var resp struct {
40    User *User
41  }
42  if err := client.Run(ctx, req, &resp); err != nil {
43    return nil, err
44  }
45  return resp.User, nil
46}

Explanation:

  • The Posts query fetches data from post-service remotely.
  • For each author, the resolver makes a remote GraphQL call to user-service.

4. Request Simulation

graphql
 1query {
 2  posts {
 3    id
 4    title
 5    author {
 6      id
 7      name
 8    }
 9  }
10}

Flow Diagram

MERMAID
sequenceDiagram
    participant CLIENT
    participant GATEWAY
    participant POST_SERVICE
    participant USER_SERVICE

    CLIENT->>GATEWAY: Query posts { id title author { id name } }
    GATEWAY->>POST_SERVICE: Query posts { id title content authorID }
    POST_SERVICE-->>GATEWAY: Posts Data
    loop For each Post
      GATEWAY->>USER_SERVICE: Query user { id name email } (with authorID)
      USER_SERVICE-->>GATEWAY: User Data
    end
    GATEWAY-->>CLIENT: List of posts & authors

5. Implications and Limitations

AspectExplanation
PerformanceFor every post, there’s a call to user-service. Limit N+1, ideally by using batching (DataLoader).
Error HandlingErrors in any one service must be handled properly in the orchestrator.
Schema ManagementSchema changes in each service must always be synced with the gateway.
Auth & ResilienceThe gateway becomes the central point for security and throttling.

6. Towards Production

The steps above represent a minimal viable architecture. For production:

  • Use DataLoader to avoid N+1 remote calls.
  • Pay attention to timeouts and circuit breakers.
  • Implement caching for specific needs.
  • Monitor aggregate latency.
  • For full federation (Apollo Federation style), keep an eye on gqlgen feature request 980 .

Conclusion

With a bit of schema stitching, we can connect multiple GraphQL services in Go without a heavy federation framework. This BFF orchestrator pattern is a great fit for small to mid-size teams that want the convenience and consistency of a global API.

As your needs grow more complex (hundreds of subgraphs, highly dynamic schemas), it’s better to keep track of native GraphQL federation developments in Go or, if feasible, mix in Node.js for the federation gateway.

Happy experimenting, and may your code be better orchestrated! 🚀


References

Related Articles

💬 Comments