published:

read time:

18-01-2021

5min

Building microservices in go part 1

Microservices in go part 1. Intro

I was seeing a lot of buzz around GO and how awesome it is for writing server-side microservices, and eventually decided to give it a shot. I had previously built this small API that translates data provided by the largest liquor store in Finland called Alko to a Graphql API. I built the service quite rapidly and it became a huge mess. This app was built using typescript and apollo-server. I'm definitely saying that the reason that the app became a mess was because of typescript, but I wanted to build this app properly and thought that this would be a great time to give GO a shot!

Building a graphql API using go and gqlgen

So I thought I would share my learnings from building this service in go. In this small series of blog posts, we are going to build a small Graphql API that talks to a PostgreSQL database. Our API will be able to store cats and their owners. I didn't really know what this API should actually do so I decided to go with cats... Since you can't go wrong with cats!

So let's get started, shall we? I will not be going over on setting up your go development environment but I will link a good resource for doing that. preparing your environment for go development

First, let's create a new folder and initialize the project

mkdir go-ms-1 && cd go-ms-1 && go mod init <path to this folder from your $GOROOT>

There is a convention in the go community to store projects under the same structure as in your external source code provider. So for example for me, the go mod init command looks like this.

go mod init github.com/japiirainen/go-ms-1

A quick word on gqlcen

Gqlgen is a Graphql implementation for go that takes a schema first approach so you should be quite familiar if you have used apollo-server before. As I was doing research about other Graphql implementations in go I noticed that most options take a code-first approach. I quite like the schema first approach so I decided to go with gqlgen.

After that, we can bootstrap our gqlgen project using the following commands.

Actually, let's first download the package!

go get github.com/99designs/gqlgen

Then we can bootstrap the project.

go run github.com/99designs/gqlgen init

This will generate quite a few files for you. Don't worry, we will go through all of these. But first, let's write some code. The very first thing we should do is write our schema for the Graphql API. I said it's schema first after all...

I like to break the schema into multiple clearly named files and not just stick everything into a single schema.graphql file. So let's do that. Go into the graph directory and create a folder called schema. This is where we will place all of our .graphql files.

Our schema folder structure will look like this.

├── schema.graphql
├── query.graphql
├── mutation.graphql
├── scalars.graphql
├── types
│   ├── cat.graphql
│   ├── owner.graphql

First lets define the types of a cat and an owner

cat.graphql

enum GENDER {
  famale
  male
}

type Cat {
  id: ID!
  breed: String!
  color: String!
  gender: GENDER!
  owner: ID!
}

owner.graphql

type Owner {
  id: ID!
  name: String!
  username: String!
  cats: [Cat]!
}

Then lets define the rest of the schema

query.graphql

type Query {
  cat(id: ID!): Cat!
  cats: [Cat!]!
  owner(id: ID!): Owner!
  owners: [Owner!]!
}

mutation.graphql

input NewOwner {
  id: ID!
  name: String!
  username: String!
}

input NewCat {
  id: ID!
  breed: String!
  color: String!
  gender: GENDER!
  owner: ID!
}

type Mutation {
  createOwner(input: NewOwner!): Owner!
  createCat(input: NewCat!): Cat!
}

schema.graphql

schema {
  query: Query
  mutation: Mutation
}

A major feature in gqlgen is that it generates go structs and resolver functions for you based on your schema. So we need to tell gqlgen where our schema files are located. And you can also delete the generated schema.graphqls file. The way you change gqlgen configuration is by modifying the gqlgen.ymk file. You can copy everything from my example if you would like to follow along.

gqlgen.yml

schema:
  - graphql/schema/**/*.graphql

exec:
  filename: graphql/generated/generated.go
  package: generated

model:
  filename: graphql/model/models_gen.go
  package: model

resolver:
  filename: ./graphql/resolvers/resolver.go
  dir: graphql/resolvers
  package: resolvers
  filename_template: "{name}.resolvers.go"

autobind:
  - "github.com/japiirainen/go-ms-1/graphql/model" CHANGE THIS LINE !

models:
  ID:
    model:
      - github.com/99designs/gqlgen/graphql.ID
      - github.com/99designs/gqlgen/graphql.Int
      - github.com/99designs/gqlgen/graphql.Int64
      - github.com/99designs/gqlgen/graphql.Int32
  Int:
    model:
      - github.com/99designs/gqlgen/graphql.Int
      - github.com/99designs/gqlgen/graphql.Int64
      - github.com/99designs/gqlgen/graphql.Int32
  Float:
    model:
      - github.com/99designs/gqlgen/graphql.Float

omit_slice_element_pointers: true

Next, you should add this line to your resolver.go file.

//go:generate go run github.com/99designs/gqlgen

I also like to use a Makefile to not have to remember all the commands I'm running frequently. So let's do that.

Makefile

gen:
    - @echo "generating..."
    - go generate -v ./graphql/...
    - @echo "done generating! ✅"

Now you should be able to run make gen and gqlgen will generate resolvers and structs for you based on our schema.

So I like to modify the file structure slightly from the default that gqlgen gives you. You don't need to copy mine if you don't want to, but I'll show it show you can copy if that's something you would like to do.

My filestructure

├── server.go
├── .env
├── go.mod
├── go.sum
├── gqlgen.yml
├── Makefile
├── graphql
│   ├── generated
│   ├── model
│   ├── resolvers
│   ├── schema

This blog post is already getting lengthy and we haven't done anything very interesting but I guess that's how it is at the start of projects. Let's at least get a simple resolver working before ending this part 1 of the project.

graphql/resolvers/owner.resolvers.go

import (
    "context"
    "fmt"

    "github.com/japiirainen/go-ms-1/graphql/model"
)

var owner *model.NewOwner

func (r *queryResolver) Owner(ctx context.Context, id string) (*model.Owner, error) {
    return &model.Owner{ID: 1, Name: "Jeppe Meikäläinen", Username: "greatUsername"}, nil
}

func (r *queryResolver) Owners(ctx context.Context) ([]model.Owner, error) {
    panic(fmt.Errorf("not implemented"))
}

func (r *mutationResolver) CreateOwner(ctx context.Context, input model.NewOwner) (*model.Owner, error) {
    panic(fmt.Errorf("not implemented"))
}

Now if we start the app by running go run --race server.g and navigating to localhost:8080 you should be able to run the following query!

{
  owner(id: 1) {
    name
    id
    cats {
      id
    }
  }
}

In the next part, we will get to some more interesting things like actually storing data in Postgres and hooking that up to our graphql resolvers. Hope this series helps you on getting started with go and graphql.

Links to the source codesource code

My website for more posts by yours truly