Go API with echo, gorm and GraphQL
TL:DR: Build a Go API with echo, gorm and GraphQL.

GraphQL has been around these days and is usually described as a new type in the field of API technologies. Even though you’ve been constructing and using RESTful API’s for quite some time, you can find it simple and useful.
In this article we’re going to highlight of how to set up GraphQL API in Go with some dependencies.
Example Repo
Here is a link to the codebase in full for reference:
Dependencies
Some dependencies are required to grab in this post so install them:
go get github.com/oxequa/realize
go get github.com/labstack/echo
go get github.com/jinzhu/gorm
go get github.com/jinzhu/gorm/dialects/mysql
go get bitbucket.org/liamstask/goose/cmd/goose
go get github.com/graphql-go/graphql
go get github.com/graphql-go/handlerSetup MySQL with Docker
Getting start with it quickly, we use Docker and setup mysql 8.0.
Add a docker-compose.yml in your root project:
version: '3'
services:
mysql:
image: mysql:8.0
volumes:
- mysqldata:/var/lib/mysql
command: mysqld --character-set-server=utf8mb4 --collation-server=utf8mb4_0900_as_ci --default-authentication-plugin=mysql_native_password
environment:
MYSQL_USER: root
MYSQL_ROOT_PASSWORD: root
ports:
- 3306:3306
volumes:
mysqldata:
driver: localAnd run it:
docker-compose upSet up echo server
Let’s set up an echo server with very simple API endpoint.
Create main.go :
package main
import (
"golang-with-echo-gorm-graphql-example/handler"
"log"
"github.com/labstack/echo"
"github.com/labstack/echo/middleware"
)
func main() {
e := echo.New()
e.Use(middleware.Logger())
e.Use(middleware.Recover())
e.GET("/", handler.Welcome())
if err := e.Start(":3000"); err != nil {
log.Fatalln(err)
}
}And add handler/handler.go :
package handler
import (
"net/http"
"github.com/labstack/echo"
)
func Welcome() echo.HandlerFunc {
return func(c echo.Context) error {
return c.String(http.StatusOK, "Welcome!")
}
}Run the code and you’ll see welcome message at http://localhost:3000 :

Set up realize
For a fast development, we can use realize for hot reloading.
In your root project, run it:
realize start --serverAnd you’ll see a .realize.yaml at the root project. To work with a echo server, edit it:
settings:
legacy:
force: false
interval: 0s
schema:
- name: golang-with-echo-gorm-graphql-example
path: .
commands:
run:
status: true
watcher:
extensions:
- go
paths:
- /
ignore:
paths:
- .git
- .realize
- vendorAnd restart it:
realize start --serverYou’ll get like this:
Running..
____ __
/ __/___/ / ___
/ _// __/ _ \/ _ \
/___/\__/_//_/\___/ v3.3.10-dev
High performance, minimalist Go web framework
https://echo.labstack.com
____________________________________O/_______
O\
⇨ http server started on [::]:3000This will watch any changes within the project and it will automatically restart the server if it detects one.
They also provide a console server at http://localhost:5201 to see what your server is going on:

Create database
Next, create a database called golang-with-echo-gorm-graphql-example_db :
CREATE DATABASE `golang-with-echo-gorm-graphql-example_db`;
Create a User table
I like using a database migration tool when I change anything on database, like Active Record does in Rails. goose is one of migration tools in Go and provides the ability of managing database schema by SQL or even Go functions.
To set it up, let’s create db/dbconfig.yml :
development:
driver: mysql
open: root:root@tcp(127.0.0.1:3306)/golang-with-echo-gorm-graphql-example_dbTo make sure of correctly connecting to:
goose statusAnd you’ll see it:
$ goose statusgoose: status for environment 'development'
Applied At Migration
=======================================
Next, create a User table through the command:
goose create CreateUsers sqlAnd you’ll see the migration file generated in db/migrations :
-- +goose Up
-- SQL in section 'Up' is executed when this migration is applied
-- +goose Down
-- SQL section 'Down' is executed when this migration is rolled backYou can add a SQL to:
-- +goose Up
-- +goose StatementBegin
CREATE TABLE users (
id INT NOT NULL AUTO_INCREMENT,
name varchar(255) DEFAULT NULL,
age varchar(255) DEFAULT NULL,
created_at datetime DEFAULT NULL,
updated_at datetime DEFAULT NULL,
deleted_at timestamp NULL DEFAULT NULL,
INDEX user_id (id),
PRIMARY KEY(id)
) ENGINE = InnoDB DEFAULT CHARSET=utf8mb4;
-- +goose StatementEnd
-- +goose Down
-- +goose StatementBegin
DROP TABLE users;
-- +goose StatementEndAnd execute the SQL:
goose upAnd a users table are created in the database:

Add some users to users table:
INSERT INTO `users` (`id`, `name`, `age`, `created_at`, `updated_at`, `deleted_at`) VALUES (1, 'name1', '20', '2019-01-01 00:00:00', '2019-01-01 00:00:00', NULL);
INSERT INTO `users` (`id`, `name`, `age`, `created_at`, `updated_at`, `deleted_at`) VALUES (2, 'name2', '30', '2019-01-01 00:00:00', '2019-01-01 00:00:00', NULL);
INSERT INTO `users` (`id`, `name`, `age`, `created_at`, `updated_at`, `deleted_at`) VALUES (3, 'name3', '40', '2019-01-01 00:00:00', '2019-01-01 00:00:00', NULL);Connect to Mysql
We mostly are done with setting up the server and the database. Next, we’ll connect to MySQL through gorm module.
Let’s create a database/db.go to connect Mysql:
package datastore
import (
"github.com/go-sql-driver/mysql"
"github.com/jinzhu/gorm"
)
func NewDB() (*gorm.DB, error) {
DBMS := "mysql"
mySqlConfig := &mysql.Config{
User: "root",
Passwd: "root",
Net: "tcp",
Addr: "127.0.0.1:3306",
DBName: "golang-with-echo-gorm-graphql-example_db",
AllowNativePasswords: true,
Params: map[string]string{
"parseTime": "true",
},
}
return gorm.Open(DBMS, mySqlConfig.FormatDSN())
}Generate a db connection in main.go :
package main
import (
"golang-with-echo-gorm-graphql-example/datastore"
"golang-with-echo-gorm-graphql-example/handler"
"log"
"github.com/labstack/echo"
"github.com/labstack/echo/middleware"
)
func main() {
db, err := datastore.NewDB()
logFatal(err)
db.LogMode(true)
defer db.Close()
e := echo.New()
e.Use(middleware.Logger())
e.Use(middleware.Recover())
e.GET("/", handler.Welcome())
err = e.Start(":3000")
logFatal(err)
}
func logFatal(err error) {
if err != nil {
log.Fatalln(err)
}
}Create a user model in domain/model/user.go :
package model
import "time"
type User struct {
ID uint `gorm:"primary_key" json:"id"`
Name string `json:"name"`
Age string `json:"age"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
DeletedAt time.Time `json:"deleted_at"`
}
func (User) TableName() string { return "users" }Add GetUsers in handler/handler.go :
func GetUsers(db *gorm.DB) echo.HandlerFunc {
return func(c echo.Context) error {
var u []*model.User
if err := db.Find(&u).Error; err != nil {
// error handling here
return err
}
return c.JSON(http.StatusOK, u)
}
}And add users endpoint to routers in main.go :
e.GET("/", handler.Welcome())
e.GET("/users", handler.GetUsers(db))Access http://localhost:3000/users and you’ll see some users result:

Set up GraphQL
Next, we’re going to transport this users endpoint to GraphQL using graphql-go and graphql-go/handler.
Let’s create graphql/handler.go :
package graphql
import (
"github.com/graphql-go/graphql"
"github.com/graphql-go/handler"
"github.com/jinzhu/gorm"
)
func NewHandler(db *gorm.DB) (*handler.Handler, error) {
schema, err := graphql.NewSchema(
graphql.SchemaConfig{
Query: newQuery(db),
},
)
if err != nil {
return nil, err
}
return handler.New(&handler.Config{
Schema: &schema,
Pretty: true,
GraphiQL: true,
}), nil
}And create graphql/query.go :
package graphql
import (
"golang-with-echo-gorm-graphql-example/graphql/field"
"github.com/graphql-go/graphql"
"github.com/jinzhu/gorm"
)
func newQuery(db *gorm.DB) *graphql.Object {
return graphql.NewObject(graphql.ObjectConfig{
Name: "Query",
Fields: graphql.Fields{
"Users": field.NewUsers(db),
},
})
}Next, create users fields, graphql/field/users.go :
package field
import (
"golang-with-echo-gorm-graphql-example/domain/model"
"github.com/graphql-go/graphql"
"github.com/jinzhu/gorm"
)
var user = graphql.NewObject(
graphql.ObjectConfig{
Name: "User",
Fields: graphql.Fields{
"id": &graphql.Field{Type: graphql.ID},
"name": &graphql.Field{Type: graphql.String},
"age": &graphql.Field{Type: graphql.String},
"createdAt": &graphql.Field{Type: graphql.String},
"updatedAt": &graphql.Field{Type: graphql.String},
"deletedAt": &graphql.Field{Type: graphql.String},
},
Description: "Users data",
},
)
func NewUsers(db *gorm.DB) *graphql.Field {
return &graphql.Field{
Type: graphql.NewList(user),
Resolve: func(p graphql.ResolveParams) (i interface{}, e error) {
var u []*model.User
if err := db.Find(&u).Error; err != nil {
// do something
}
return u, nil
},
Description: "user",
}
}And replace the users endpoint with graphql in main.go :
package main
import (
"golang-with-echo-gorm-graphql-example/datastore"
"golang-with-echo-gorm-graphql-example/graphql"
"golang-with-echo-gorm-graphql-example/handler"
"log"
"github.com/labstack/echo"
"github.com/labstack/echo/middleware"
)
func main() {
db, err := datastore.NewDB()
logFatal(err)
db.LogMode(true)
defer db.Close()
e := echo.New()
e.Use(middleware.Logger())
e.Use(middleware.Recover())
e.GET("/", handler.Welcome())
// graphql endpoint here
h, err := graphql.NewHandler(db)
logFatal(err)
e.POST("/graphql", echo.WrapHandler(h))
err = e.Start(":3000")
logFatal(err)
}
func logFatal(err error) {
if err != nil {
log.Fatalln(err)
}
}To work with echo handler, wrap graphql hander with echo.WrapHandler .
It should get the users at http://localhost:3000/query through POST. You’ll see at GraphiQL:

Conclusion
That’s it. We got very simple users endpoint and learned how to resolve it through echo and GraphQL. Hopefully this will be useful to help you get off the ground with GraphQL API in Go.
You can view the final working example at:
