I wonder how to implement GraphQL in Golang. I tried to search and found some libraries that would help me. Here is the list:
- graphql-go. I use this to help me to implement the GraphQL.
- echo. I use this library to help me implement the routing and HTTP handler.
- gorm. I use this library to help me implement the database side. This library is an ORM.
Code ???
I'll recommend you to visit my Github repository.
Here is the code:
package main
import (
"database/sql"
"fmt"
"log"
"net/http"
"os"
"time"
"github.com/graphql-go/graphql"
"github.com/labstack/echo/v4"
"gorm.io/driver/postgres"
"gorm.io/gorm"
)
type PostData struct {
Query string `json:"query"`
Operation string `json:"operation"`
Variables map[string]interface{} `json:"variables"`
}
type User struct {
ID uint
Name string
Email *string
Age uint8
Birthday *time.Time
MemberNumber sql.NullString
ActivatedAt sql.NullTime
CreatedAt time.Time
UpdatedAt time.Time
}
func executeQuery(query string, schema graphql.Schema) *graphql.Result {
result := graphql.Do(graphql.Params{
Schema: schema,
RequestString: query,
})
if len(result.Errors) > 0 {
fmt.Printf("wrong result, unexpected errors: %v", result.Errors)
}
return result
}
func main() {
dsn := os.Getenv("DB_CONNECTION_STRING")
db, err := gorm.Open(postgres.Open(dsn), &gorm.Config{})
if err != nil {
log.Fatalf("failed to connect database, error: %v", err)
}
db.AutoMigrate(&User{})
// Schema
userType := graphql.NewObject(
graphql.ObjectConfig{
Name: "User",
Fields: graphql.Fields{
"id": &graphql.Field{
Type: graphql.Int,
},
"name": &graphql.Field{
Type: graphql.String,
},
},
},
)
fields := graphql.Fields{
"hello": &graphql.Field{
Type: graphql.String,
Resolve: func(p graphql.ResolveParams) (interface{}, error) {
return "world", nil
},
},
}
mutationFields := graphql.Fields{
"createUser": &graphql.Field{
Type: userType,
Description: "Create New User",
Args: graphql.FieldConfigArgument{
"name": &graphql.ArgumentConfig{
Type: graphql.NewNonNull(graphql.String),
},
},
Resolve: func(params graphql.ResolveParams) (interface{}, error) {
user := User{
Name: params.Args["name"].(string),
}
result := db.Create(&user)
if result.Error != nil {
return nil, result.Error
}
return user, nil
},
},
}
rootQuery := graphql.ObjectConfig{Name: "RootQuery", Fields: fields}
rootMutation := graphql.ObjectConfig{Name: "RootMutation", Fields: mutationFields}
schemaConfig := graphql.SchemaConfig{
Query: graphql.NewObject(rootQuery),
Mutation: graphql.NewObject(rootMutation),
}
schema, err := graphql.NewSchema(schemaConfig)
if err != nil {
log.Fatalf("failed to create new schema, error: %v", err)
}
e := echo.New()
e.GET("/", func(c echo.Context) error {
return c.String(http.StatusOK, "Hello, World!")
})
e.POST("/graphql", func(c echo.Context) error {
data := new(PostData)
if err := c.Bind(data); err != nil {
return err
}
result := graphql.Do(graphql.Params{
Schema: schema,
RequestString: data.Query,
VariableValues: data.Variables,
OperationName: data.Operation,
})
return c.JSON(http.StatusOK, result)
})
e.Logger.Fatal(e.Start(":1323"))
}
TLDR
Important Parts
We will have 3 important parts. Let's go.
- Initiate Echo, define GraphQL Path and common execution to call GraphQL Query or GraphQL Mutation.
// data struct to represent the request of GraphQL.
type PostData struct {
Query string `json:"query"`
Operation string `json:"operation"`
Variables map[string]interface{} `json:"variables"`
}
func main() {
// ... rest of codes
// initiate echo
e := echo.New()
// initiate path and handler
e.POST("/graphql", func(c echo.Context) error {
// bind body message of query/mutation to PostData
data := new(PostData)
if err := c.Bind(data); err != nil {
return err
}
// call the graphql using the data provided and already binded with PostData
result := graphql.Do(graphql.Params{
Schema: schema,
RequestString: data.Query,
VariableValues: data.Variables,
OperationName: data.Operation,
})
// return the result as it is and expect will success
// we will learn in the next post about error handling
return c.JSON(http.StatusOK, result)
})
e.Logger.Fatal(e.Start(":1323"))
}
- Initiate Database and ORM
// sample table definition
type User struct {
ID uint
Name string
Email *string
Age uint8
Birthday *time.Time
MemberNumber sql.NullString
ActivatedAt sql.NullTime
CreatedAt time.Time
UpdatedAt time.Time
}
func main() {
// you may change the os.Getenv to string directly for testing in case you don't have the env.
dsn := os.Getenv("DB_CONNECTION_STRING")
// initiate the database connection
// since I use postgres, so the open come from postgres driver, you may use other drivers if you are using another database.
db, err := gorm.Open(postgres.Open(dsn), &gorm.Config{})
if err != nil {
log.Fatalf("failed to connect database, error: %v", err)
}
// we are doing auto migrate
db.AutoMigrate(&User{})
// ... rest of codes
}
- Initiate GraphQL Schema, Queries, and Mutations
func main() {
// ... rest of codes
// initiate user object
userType := graphql.NewObject(
graphql.ObjectConfig{
Name: "User",
Fields: graphql.Fields{
"id": &graphql.Field{
Type: graphql.Int,
},
"name": &graphql.Field{
Type: graphql.String,
},
},
},
)
// sample to define queries
fields := graphql.Fields{
"hello": &graphql.Field{
Type: graphql.String,
Resolve: func(p graphql.ResolveParams) (interface{}, error) {
return "world", nil
},
},
}
// sample to define mutations
mutationFields := graphql.Fields{
"createUser": &graphql.Field{
Type: userType,
Description: "Create New User",
Args: graphql.FieldConfigArgument{
"name": &graphql.ArgumentConfig{
Type: graphql.NewNonNull(graphql.String),
},
},
Resolve: func(params graphql.ResolveParams) (interface{}, error) {
user := User{
Name: params.Args["name"].(string),
}
result := db.Create(&user)
if result.Error != nil {
return nil, result.Error
}
return user, nil
},
},
}
// merge the queries into rootQuery
rootQuery := graphql.ObjectConfig{Name: "RootQuery", Fields: fields}
// merge the mutation into rootMutation
rootMutation := graphql.ObjectConfig{Name: "RootMutation", Fields: mutationFields}
// merge query & mutation to 1 schema
schemaConfig := graphql.SchemaConfig{
Query: graphql.NewObject(rootQuery),
Mutation: graphql.NewObject(rootMutation),
}
// initiate the schema
schema, err := graphql.NewSchema(schemaConfig)
if err != nil {
log.Fatalf("failed to create new schema, error: %v", err)
}
// ... rest of codes
}
Thank you
I just want to share what I've learned this week. I learn a lot and try to understand about go, especially if I want to code with GraphQL. I hope you enjoy my sharing. If you have any questions, let me know. We will learn together. Thank you!
Other Related Articles
You might want to see these articles too.
Top comments (0)