go-microservice-boilerplate
Go Microservice Boilerplate
Environment
DB_CONNECTION_STRING=
PORT=
Build
go build
Run Locally
go run server.go
Test
go test ./... -cover
LICENSE
MIT
I wonder how to implement GraphQL in Golang. I tried to search and found some libraries that would help me. Here is the list:
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"))
}
We will have 3 important parts. Let's go.
// 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"))
}
// 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
}
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
}
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!
You might want to see these articles too.