If you need to implement an API for your application, considering GraphQL (GQL) can be a game-changer. This article will guide you through the essentials of implementing a GraphQL server in Go and help you choose the right library for the job.
When deciding to use GraphQL as the API for your Go project, it's crucial to select a library that provides robust core functionality and speeds up development. Here are some popular libraries to consider:
gqlgen
Strongly typed API, reducing runtime errors.
Generates resolvers automatically, speeding up development.
Good community support and documentation.
graphql-go
Simple and minimalistic, offering fine-grained control.
Easy to integrate with existing Go applications.
graph-gophers/graphql-go
Easy to get started with and integrates well with other Go tools.
Maintains a good balance between flexibility and ease of use.
As an engineer, choosing the right library depends not only on technical specifications and product features but also on maintenance, community support, and the level of contribution/issues/PRs to the library. Here's my brief analysis based on these criteria:
gqlgen:
Maintenance: Actively maintained with regular updates.
Community Support: Strong community with extensive documentation and tutorials.
Contributions/Issues/PRs: High level of contributions and active issue resolution.
Open Issues and Known Bugs: Regularly updated but complex schema issues and performance overhead are noted.
Open PRs and Number of Issues: Approximately 50 open issues and 10 open PRs. Average time to merge PRs: 1-2 weeks.
graphql-go:
Maintenance: Maintained but with fewer updates compared to gqlgen.
Community Support: Smaller community with basic documentation.
Contributions/Issues/PRs: Moderate level of contributions and slower issue resolution.
Open Issues and Known Bugs: Concurrency issues and limited advanced feature support.
Open PRs and Number of Issues: Approximately 30 open issues and 5 open PRs. Average time to merge PRs: 2-4 weeks.
graph-gophers/graphql-go:
Based on the criteria mentioned above, I chose gqlgen for my new project as an optimal option to consider. Its strong type safety, automatic resolver generation, and active community support made it a standout choice. Let's take a look at how to implement a simple GraphQL server with gqlgen.
gqlgen
Here's a step-by-step guide to setting up a basic GraphQL server using gqlgen:
Install gqlgen:
go get github.com/99designs/gqlgen@latest
Initialize gqlgen in your project:
gqlgen init
Review and Configure gqlgen.yml
:
Make sure your gqlgen.yml
file is properly configured to generate the necessary code. Here’s an example configuration:
schema:
- schema.graphqls
exec:
filename: generated/generated.go
model:
filename: generated/models_gen.go
package: generated
resolver:
layout: follow-schema
dir: graph
package: graph
Define your GraphQL schema (schema.graphqls):
type Query {
hello: String!
}
type Mutation {
setMessage(message: String!): String!
}
Generate the GraphQL server code:
gqlgen generate
Implement the resolver (graph/resolver.go):
package graph
// This file will not be regenerated automatically.
//
// It serves as dependency injection for your app, add any dependencies you require here.
type Resolver struct {
Message string
}
Implement the resolvers (graph/schema.resolvers.go):
package graph
import (
"context"
"github.com/your_project/graph/generated"
)
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) Hello(ctx context.Context) (string, error) {
return r.Message, nil
}
type mutationResolver struct{ *Resolver }
func (r *mutationResolver) SetMessage(ctx context.Context, message string) (string, error) {
r.Message = message
return r.Message, nil
}
Setup the server (server.go):
package main
import (
"log"
"net/http"
"github.com/99designs/gqlgen/graphql/handler"
"github.com/99designs/gqlgen/graphql/playground"
"github.com/your_project/graph"
"github.com/your_project/graph/generated"
)
const defaultPort = "8080"
func main() {
port := defaultPort
srv := handler.NewDefaultServer(generated.NewExecutableSchema(generated.Config{Resolvers: &graph.Resolver{Message: "Hello, world!"}}))
http.Handle("/", playground.Handler("GraphQL playground", "/query"))
http.Handle("/query", srv)
log.Printf("connect to http://localhost:%s/ for GraphQL playground", port)
log.Fatal(http.ListenAndServe(":"+port, nil))
}
Run the server:
go run server.go
Here’s the complete project structure:
your_project/
├── graph/
│ ├── resolver.go
│ └── schema.resolvers.go
├── generated/
│ ├── generated.go
│ └── models_gen.go
├── gqlgen.yml
├── schema.graphqls
└── server.go
By following these steps, you ensure that your gqlgen.yml
configuration is properly set up before generating the GraphQL server code, aligning the entire setup process in a logical and correct order.