Skip to content
Santekno.com | Tech Tutorials and Trends
EN
📖 0%
04 Jul 2025 · 5 min read ·Article 4 / 125
Go

4 GraphQL Server Architectures in Go

IH
Ihsan Arif
Writer at Santekno · Backend Engineer

4 GraphQL Server Architectures in Go

GraphQL has become the new standard for building scalable and flexible APIs. The Go (Golang) ecosystem itself, while often associated with REST, actually already provides plenty of tools and architectural options for building a production-ready GraphQL server.

In this article, I’ll walk through four GraphQL server architectures commonly used in Go, complete with code examples, simulations, and flow diagrams using Mermaid to make the visualizations easier to grasp.


1. Monolithic GraphQL Server

Description
The monolithic architecture is the simplest pattern—all of the business logic, resolvers, and data access live in a single codebase/service. In Go, popular libraries such as graph-gophers/graphql-go or 99designs/gqlgen are typically used.

When to use it:

  • Small to medium-scale projects
  • Small teams
  • MVPs or prototyping

Advantages:

  • Easy to develop and deploy
  • Low overhead
  • Simple debugging

Drawbacks:

  • Hard to scale
  • Tight coupling between resolvers and data

Simple code example (using gqlgen):

go
1// go.mod
2module monolith-graphql
3
4go 1.18
5
6require (
7    github.com/99designs/gqlgen v0.17.24
8)
graphql
1# schema.graphqls
2type Query {
3  hello: String!
4}
go
1// resolver.go
2package graph
3
4type Resolver struct{}
5
6func (r *Resolver) Query_hello() string {
7    return "Hello from Monolithic GraphQL!"
8}
go
 1// main.go
 2package main
 3
 4import (
 5    "log"
 6    "net/http"
 7    "monolith-graphql/graph"
 8    "github.com/99designs/gqlgen/graphql/handler"
 9    "github.com/99designs/gqlgen/graphql/playground"
10)
11
12func main() {
13    srv := handler.NewDefaultServer(NewExecutableSchema(Config{Resolvers: &graph.Resolver{}}))
14    http.Handle("/", playground.Handler("GraphQL playground", "/query"))
15    http.Handle("/query", srv)
16    log.Fatal(http.ListenAndServe(":8080", nil))
17}

Flow Diagram

MERMAID
graph TD;
    User-->GraphQLServer
    GraphQLServer-->Resolver
    Resolver-->Database

2. Schema Stitching

Description
Once your API starts growing and spans many domains (user, product, payment), you can split the schema and its logic into several modules, then combine them using schema stitching. In Go, graphql-go-tools from WunderGraph or gqlmerge can be used to perform the stitching.

When to use it:

  • Large teams working on different domains
  • A codebase you want to keep modular
  • Scaling at the team/organization level

Advantages:

  • Easy to maintain per domain
  • Schema modules can be reused

Drawbacks:

  • Resolver registration is more complicated
  • Versions and dependencies must be carefully controlled

Code example (simulating the modular pattern):

text
1graph
2├── user/
3│   ├── schema.graphqls
4│   └── resolver.go
5├── product/
6│   ├── schema.graphqls
7│   └── resolver.go
8├── main.go

graphql
1# user/schema.graphqls
2type Query {
3  user(id: ID!): User
4}
5type User { id: ID!, name: String! }
graphql
1# product/schema.graphqls
2type Query {
3  product(id: ID!): Product
4}
5type Product { id: ID!, name: String! }
go
 1// main.go (fragment)
 2import (
 3    // ...
 4    "github.com/wundergraph/graphql-go-tools/pkg/executor"
 5    userSchema "yourapp/user/schema.graphqls"
 6    productSchema "yourapp/product/schema.graphqls"
 7)
 8
 9func main() {
10    mergedSchema := mergeSchemas(userSchema, productSchema)
11    exec := executor.NewExecutor(mergedSchema)
12    // Bind to HTTP handler
13}

Flow Diagram

MERMAID
graph TB;
    User-->UserService
    Product-->ProductService
    UserService-->SchemaStitchingLayer
    ProductService-->SchemaStitchingLayer
    SchemaStitchingLayer-->Client

3. Federation (Apollo Federation Inspired)

Description
Federation is an architecture in which each domain/service runs its own GraphQL server with a composable schema. There is then a single Gateway (Apollo Gateway, or your own implementation in Go) that merges the schemas and becomes the main entrypoint for clients.

In Go, you can use gqlgen-federation .

When to use it:

  • Microservices
  • Per-domain ownership
  • Multi-team/organization setups

Advantages:

  • Scalability
  • Independence per team/domain
  • You can deploy a service without taking the gateway down

Drawbacks:

  • More complex (infra & devops)
  • Consistency and dependencies between services
  • Latency increases when the service chain is long

Simulated Code Example:

go
1// product/schema.graphqls
2type Product @key(fields: "id") {
3  id: ID!
4  name: String!
5}
6extend type Query {
7  product(id: ID!): Product
8}
go
1// user/schema.graphqls
2type User @key(fields: "id") {
3  id: ID!
4  name: String!
5  purchasedProducts: [Product]
6}
7extend type Query {
8  user(id: ID!): User
9}
Each service is then run with its own server. The Gateway reads the service registry and composes the schema automatically (for example, Apollo Gateway).

Flow Diagram

MERMAID
graph TD;
    Client-->GraphQLGateway
    GraphQLGateway-->UserGraphQLService
    GraphQLGateway-->ProductGraphQLService

4. BFF (Backend for Frontend) Pattern

Description
BFF is a pattern in which a single GraphQL server is designed specifically for the needs of a particular frontend/app (for example: mobile vs. web). In Go, GraphQL is often used as a data aggregation layer pulling from various backends (REST, RPC, DB, and so on).

When to use it:

  • A single frontend with unique requirements
  • You want to aggregate multiple backends
  • Optimizing the query/response for one specific consumer

Advantages:

  • A highly tailored API for one frontend
  • You can optimize security and caching
  • Field usage tracking is easier to monitor

Drawbacks:

  • Not reusable across frontends
  • Schema must be synced whenever a new frontend is added

Code Example:
We’ll aggregate data from a REST API and a database, then expose it via GraphQL:

go
 1// resolver.go (pseudo code)
 2func (r *Resolver) Query_user(ctx context.Context, id string) (*User, error) {
 3    // Fetch from REST
 4    resp, _ := http.Get(fmt.Sprintf("https://api.mycompany.com/users/%s", id))
 5    var userData User
 6    json.NewDecoder(resp.Body).Decode(&userData)
 7    
 8    // Fetch from DB (e.g., list of orders)
 9    orders := db.GetOrdersForUser(id)
10    return &User{...userData, Orders: orders}, nil
11}

Comparison Simulation (Table)

PatternTopologyWhen to UseScalabilityEasiest to Build?Suitability
MonolithSingle serviceSmall project, MVPLowYesTight team, low overhead
Schema StitchingModular, 1 processMedium-large, domain specMediumMediumSeveral domains, team
FederationMicroservices + GatewayOrg-wide, multi-productHighNoLarge org, microservices
BFF1:1 for each frontendPer app/frontendMediumMediumTailored, per app

Conclusion

Building a GraphQL server in Go is highly flexible—you can start from a monolith, then refactor toward a modular, federation, or BFF approach depending on your business needs and team growth.

Rule of thumb:

  • Start simple and evolve toward more complex patterns as scale and requirements grow.
  • Go is more than capable for production GraphQL—choose the library and architecture that fit your project’s phase and your team’s expertise.

References


Suggestions? Feedback? Experiences migrating GraphQL architectures in Go? Feel free to share them in the comments!


Happy coding! 🚀

Related Articles

💬 Comments