Subscriptions are a GraphQL feature that allows a server to send data to its clients when a specific event happens. Subscriptions are usually implemented with WebSockets.
In this chapter you will bring realtime functionality to your GraphQL API by implementing GraphQL subscriptions.
The goal of this chapter is to create a subscription to the comments posted on a Meetup.com Event.
Let's add some new types. As before, you can take a look to the model located in the github.com/smoya/graphql-go-workshop/pkg/meetup package.
Add the following types:
type Comment {
id: Int!
comment: String!
created: String!
likes: Int!
member: Member!
}
type Subscription {
commentPosted(groupName: String!, eventID: String!): Comment!
}
Before generating the code again, we would need to add few new lines to the gqlgen.yaml
file.
Please add the following lines under model
.
Comment:
model: github.com/smoya/graphql-go-workshop/pkg/meetup.Comment
Note The
resolver.go
file is generated only when the file is not present.
As the generator will end creating new Resolvers, you would need to choose 1 option from:
- Moving the
resolver.go
file to aresolver.go.copy
before executinggqlgen
for later "copy and paste" the needed code to the generated code. - Removing the
resolver.go
file and redo all the previous steps.
gqlgen
would generate a new resolver called commentResolver
.
You would need to implement some methods:
// Comment returns a CommentResolver.
func (r *Resolver) Comment() CommentResolver {
return &commentResolver{r}
}
type commentResolver struct{ *Resolver }
func (commentResolver) Likes(ctx context.Context, obj *meetup.Comment) (int, error) {
return obj.LikeCount, nil
}
func (commentResolver) Created(ctx context.Context, obj *meetup.Comment) (string, error) {
return time.Unix(obj.Created/1000, 0).Format(time.RFC822), nil
}
gqlgen
would generate a new resolver called subscriptionResolver
.
You would need to implement some methods:
// Subscription returns a SubscriptionResolver
func (r *Resolver) Subscription() SubscriptionResolver {
return &subscriptionResolver{r}
}
type subscriptionResolver struct{ *Resolver }
func (r *subscriptionResolver) CommentPosted(ctx context.Context, groupName string, eventID string) (<-chan meetup.Comment, error) {
// ideally this should go in to a redis or similar.
sentComments := make(map[int]struct{})
commentsChan := make(chan meetup.Comment)
// ideally this should be configurable
t := time.NewTicker(time.Second * 5)
go func() {
for {
select {
case <-ctx.Done():
return
case <-t.C:
comments, err := r.C.Comments(groupName, eventID)
if err != nil {
log.Printf("error finding comments for group %s and event %s. %s\n", groupName, eventID, err.Error())
continue
}
for _, c := range comments {
if _, ok := sentComments[c.ID]; !ok {
commentsChan <- *c
sentComments[c.ID] = struct{}{}
}
}
}
}
}()
return commentsChan, nil
}
Run go run server/server.go
.
Open your browser and navigate to http://localhost:8080.
Execute the following subscription:
subscription{
commentPosted(groupName: "Golang-Barcelona", eventID: "256537826"){
id
comment
created
likes
member {
name
}
}
}