GraphQL Subscription with Golang and React JS – Part 1

Author - Bipin Fultariya

Learning GraphQL Subscription in Golang and React JS

For this GraphQL Subscription demo we have used Golang gqlgen package, mysql and React JS Apollo-Client.

Purpose of this demo is to showcase graphql query, mutation and GraphQL subscription in golang.

Although original documentation of gqlgen is good but I thought it would be great to do CRUD separately for learning purpose. I hope this will be great help to someone starting Golang GraphQL.

I am considering that reader has some basic knowledge of Golang and database connection in Golang.

This is part-1 of two series tutorial

  1. Golang gqlgen backend
  2. React JS apollo-client frontend which is using this Golang backend.
Golang React Subscription

What is gqlgen?

It’s Golang package cum utility which allow us to auto generate Golang code stub from GraphQL schema automatically.

Their original getting started document is good to read but I will mention steps here also.

Step 1:

  • Install gqlgen
go get -u github .com/99designs/gqlgen github .com/vektah/gorunpkg
  • All the code we will be doing should reside in go/src folder.
    For purpose of this demo, this is the preferred path :-    ~/go/src/golang-gqlgen-reactjs-subscription-demo/golang

Step 2:

  • So first we need to create our required GraphQL schema.
    Create schema.graphql file with below:
type Channel {
    id: Int!  # "!" denotes a required field
    name: String!
}

type Query {
    channels: [Channel!]!
}

type Mutation {
    addChannel(name: String!): Channel!
    updateChannel(id:Int!,name: String!): Channel!
    deleteChannel(ID: Int!): Channel!
}

type Subscription {
    subscriptionChannelAdded: Channel!
    subscriptionChannelDeleted: Channel!
    subscriptionChannelUpdated: Channel!
}

Then next file we need is gqlgen.yml
This file is responsible to direct gqlgen about where to auto generate files. For our demo purpose content of that file will be as below:-

schema: schema.graphql
exec:
    filename: app/graph/generated.go

model:
    filename: app/model/models_gen.go

resolver:
    filename: app/resolver/resolver.go
    type: Resolver

Step 3:

Inside our mentioned folder fire below command :-

$ gqlgen

This will generate below files if you are running it for the first time.
app/graph/generated.go
app/model/models_gen.go
app/resolver/resolver.go (if this file exists it will not be regenerated)

Step 4:

So first thing will be required for us is GraphQL query to get all channels.

As we have followed little different structure for ease of understanding and maintenance.
Create file app/resolver/ChannelResolver.go

func (r *queryResolver) Channels(ctx context.Context) 
                ([]model.Channel, error) {
    db := connection.DbConn()
    var query = "SELECT * FROM channel"
    selDB, err := db.Query(query)
    var arrChannel []model.Channel
    for selDB.Next() {
        var name string
        var id int64
        err = selDB.Scan(&id, &name)
        if err != nil {
            panic(err.Error())
        }
        todo1 := model.Channel{ID: int(id), Name: name}
        arrChannel = append(arrChannel, todo1)
    }

    defer db.Close()
    return arrChannel, nil
}

Don’t forget to remove below function from resolver.go file
Channels(ctx context.Context) ([]model.Channel, error)

Step 5:

In this section I will mention Add Channel Subscription and Mutation. Although Update and Delete subscription and Mutation is added in GitHub source code.

First let’s create subscription. For our subscription to work we need subscription observer array which will hold all connected client subscription observer. Which will allow us to fire event on AddChannle Mutation.

func init() {
    addChannelObserver = map[string]chan model.Channel{}
}

Below is function which adds new client observer to our subscription array.

func (r *subscriptionResolver) SubscriptionChannelAdded
           (ctx context.Context) (<-chan model.Channel, error) {
    id := randString(8)
    events := make(chan model.Channel, 1)

    go func() {
        <-ctx.Done()
        delete(addChannelObserver, id)
    }()

    addChannelObserver[id] = events

    return events, nil
}

Don’t forget to remove below function from resolver.go file
SubscriptionChannelAdded
(ctx context.Context)

Now only remaining thing is AddChannel Mutation. Once channel is saved in db(mysql) it fire up event to all listening client via our channel added subscription.

func (r *mutationResolver) AddChannel(ctx context.Context, name string) 
(model.Channel, error) {
    db := connection.DbConn()

    insForm, err := db.Prepare("INSERT INTO channel(name) VALUES(?)")
    if err != nil {
        panic(err.Error())
    }

    var newChannel model.Channel
    res, err := insForm.Exec(name)
    if err != nil {
        println("Exec err:", err.Error())
    } else {
        var id int64
        id, err := res.LastInsertId()
        if err != nil {
            println("Error:", err.Error())
        } else {
            newChannel = model.Channel{ID: int(id), Name: name}
        }
    }
    defer db.Close()
    // Add new channel in addChannelObserver
    for _, observer := range addChannelObserver {
        observer <- newChannel 
        // This sends new channel to client via socket
    }
    return newChannel, nil
}

Don’t forget to remove below function from resolver.go file
AddChannel(ctx context.Context, name string)
And that’s it.

Get a source code from GitHub Link below and run it
$ cd golang
$ go get ./
$ go run main.go

Golang server will be running at http://localhost:8888

If you have followed this so far then you must be crazy for Golang and GraphQL ?

I hope this will be helpful for the people struggling with Golang GraphQL subscription.

golang-gqlgen-reactjs-subscription-demo

Don’t miss the next post!

Loading

Related Posts