Building a GraphQL API – GraphQL API example in Go

Introduction to GraphQL

GraphQL is a query language for APIs and a runtime for fulfilling those queries with your existing data. GraphQL provides a complete and understandable description of the data in your API, gives clients the power to ask for exactly what they need and nothing more, makes it easier to evolve APIs over time, and enables powerful developer tools.

Unlike traditional REST APIs, which expose multiple endpoints to retrieve different types of data, GraphQL allows you to request exactly the data you need in a single query. This approach can simplify client-server interactions, improve performance, and enhance the flexibility of your API.

GraphQL API Vs REST API

  • GraphQL is Client-Driven, whereas REST is Server-Driven.
  • Queries are organized in terms of Schema and strict typecasting in GraphQL, whereas REST has Endpoints for that task.
  • GraphQL calls Specific data with a single call. REST calls Fixed data with multiple calls.
  • Instead of the GET, POST, PUT, DELETE operations in REST, GraphQL has Query, Mutation, and Subscription for data manipulation.

Key Concepts of GraphQL

1. Schema

The GraphQL schema defines the structure of the API. It specifies what queries and mutations are available, the types of data they return, and how these types are related.

  • Types: The schema defines various types of data, such as User, Post, and Comment. Each type can have fields with different types (e.g., String, Int, Boolean).
  • Queries: Define what data can be fetched from the API. For example, you might have a getUser query that retrieves a user by their ID.
  • Mutations: Define operations that modify data on the server. For example, createUser might be used to add a new user to the system.
  • Subscriptions: Enable real-time updates to clients. For example, a postAdded subscription might notify clients when a new post is created.

2. Types of GraphQL Types

  • Scalars: Basic data types such as String, Int, Float, Boolean, and ID.
  • Objects: Complex types with fields. For instance, a User type might have fields like id, name, and email.
  • Enums: Enumerated types with a predefined list of values. For example, a Status enum might include values like ACTIVE, INACTIVE, and PENDING.
  • Interfaces: Abstract types that can be implemented by other types. For example, a Person interface might be implemented by User and Admin.
  • Unions: Types that represent one of several possible types. For example, a SearchResult union might include User and Post.
  • Input Objects: Complex input types used for arguments in queries and mutations. For example, an AddUserInput might include fields like name and email.

GraphQL with `gqlgen` library in Go

Overview of gqlgen

`gqlgen` is a powerful library for building GraphQL servers in Go. It automates much of the boilerplate code required for setting up a GraphQL server by generating Go code from your GraphQL schema.

Setting Up gqlgen

  1. Install gqlgen: First, you need to install gqlgen if you haven’t already. You can do this using Go modules:
go get github.com/99designs/gqlgen

2. Define Your Schema

Create a GraphQL schema file (schema.graphql) defining your types, queries, and mutations. For example:

# schema.graphql
type Query {
  getUser(id: ID!): User
  listUsers: [User!]!
}

type Mutation {
  createUser(name: String!): User!
}

type User {
  id: ID!
  name: String!
}

3. Generate Code

Initialize gqlgen and generate the necessary boilerplate code:

gqlgen init

This command generates the necessary files, including:

  • resolver.go for your resolver implementations.
  • models_gen.go for auto-generated models.
  • generated.go for the generated GraphQL code.

4. Implement Resolvers

Define your resolver functions in resolver.go. Here’s an example implementation:

// resolver.go
package graph

import (
  "context"
  "github.com/yourusername/yourproject/graph/generated"
  "github.com/yourusername/yourproject/graph/model"
)

type Resolver struct {
  users map[string]*model.User
}

func (r *Resolver) Query() generated.QueryResolver {
  return &queryResolver{r}
}

func (r *Resolver) Mutation() generated.MutationResolver {
  return &mutationResolver{r}
}

type queryResolver struct{ *Resolver }

func (r *queryResolver) GetUser(ctx context.Context, id string) (*model.User, error) {
  user, exists := r.users[id]
  if !exists {
    return nil, nil // Or return an error
  }
  return user, nil
}

func (r *queryResolver) ListUsers(ctx context.Context) ([]*model.User, error) {
  var userList []*model.User
  for _, user := range r.users {
    userList = append(userList, user)
  }
  return userList, nil
}

type mutationResolver struct{ *Resolver }

func (r *mutationResolver) CreateUser(ctx context.Context, name string) (*model.User, error) {
  id := "1" // Generate a unique ID in real applications
  user := &model.User{
    ID:   id,
    Name: name,
  }
  r.users[id] = user
  return user, nil
}

5. Run Your Server

Set up and run your server using the generated code and resolver implementations:

package main

import (
  "log"
  "net/http"
  "github.com/99designs/gqlgen/graphql/handler"
  "github.com/99designs/gqlgen/graphql/playground"
  "github.com/yourusername/yourproject/graph/generated"
  "github.com/yourusername/yourproject/graph"
)

func main() {
  srv := handler.NewDefaultServer(generated.NewExecutableSchema(generated.Config{Resolvers: &graph.Resolver{}}))

  http.Handle("/", playground.Handler("GraphQL playground", "/query"))
  http.Handle("/query", srv)

  log.Fatal(http.ListenAndServe(":8080", nil))
}

To run GraphQL queries locally, follow these steps:

  1. Access the Playground: Open your web browser and navigate to http://localhost:8080/playground. This URL takes you to the GraphQL Playground, an interactive environment for testing and exploring your GraphQL API.
  2. Write Your Query: In the Playground interface, you can write GraphQL queries, mutations, or subscriptions.
  3. Execute the Query: Click the “Play” button (usually a triangle icon) to execute the query. The results will be displayed in the right-hand panel, showing the data retrieved from your GraphQL server.
  4. Review Results: Examine the response to ensure it matches your expectations. The Playground provides a detailed view of the results, including any errors or data returned from the query.

GraphQL queries and mutations for the provided schema and resolvers:

  1. Query to Get a User by ID – his query retrieves the user with the specified ID (1 in this example). The response will include the id and name fields of the user.
query {
  getUser(id: "1") {
    id
    name
  }
}

2. Query to List All Users – This query retrieves a list of all users, including their id and name. It will return an array of user objects.

query {
  listUsers {
    id
    name
  }
}

3. Mutation to Create a New User – This mutation creates a new user with the name "John Doe". The response will include the id and name of the newly created user.

mutation {
  createUser(name: "John Doe") {
    id
    name
  }
}

You may also like

Leave a Reply