Other than REST API, there is also an option to create an API server. For this article, I will use gqlgen library to initialize a GraphQL server.
First, create a Go module and initialize the project with gqlgen
mkdir graphql-api
cd graphql-api
go mod init gographql
go get github.com/99designs/gqlgen
go run github.com/99designs/gqlgen init
Then you will see gqlgen has generated many files for you.
Then let's edit the graph/schema.graphqls
file
type User {
id: ID!
name: String!
age: Int!
}
type UserUpdated {
user: User!
info: String!
}
type Query {
users: [User!]!
user(id: ID!): User!
}
input CreateUserInput {
name: String!
age: Int!
}
type Mutation {
createUser(input: CreateUserInput!): User!
deleteUser(id: ID!): User!
}
type Subscription {
userUpdated: UserUpdated!
}
Then regenerate the graphql Go files
go get github.com/99designs/gqlgen
go run github.com/99designs/gqlgen generate
In the graph/schema.resolvers.go
file, you can delete TODO handlers that are in the bottom of the file.
Next, create store/store.go
file to build a data structure to save user information; then insert this code
package store
import (
"errors"
"gographql/graph/model"
"strconv"
"github.com/google/uuid"
)
type UserStore struct {
users []*model.User
}
func NewUserStore() *UserStore {
return &UserStore{}
}
func (s *UserStore) Save(name string, age int) *model.User {
id := uuid.New().ID()
user := &model.User{
ID: strconv.FormatUint(uint64(id), 10),
Name: name,
Age: age,
}
s.users = append(s.users, user)
return user
}
func (s *UserStore) Delete(id string) (*model.User, error) {
pos := -1
for i, v := range s.users {
if v.ID == id {
pos = i
break
}
}
if pos == -1 {
return nil, errors.New("user id not found")
}
user := s.users[pos]
s.users = append(s.users[:pos], s.users[pos+1:]...)
return user, nil
}
func (s *UserStore) FindByID(id string) (*model.User, error) {
for _, v := range s.users {
if v.ID == id {
return v, nil
}
}
return nil, errors.New("user id not found")
}
func (s *UserStore) GetAllUsers() []*model.User {
return s.users
}
Then register the store in the graph/resolver.go
file
type Resolver struct {
mu sync.Mutex
UserSubscribers map[string]chan *model.UserUpdated
UserStore *store.UserStore
}
Also change the srv
variable in main.go
file
srv := handler.NewDefaultServer(
generated.NewExecutableSchema(
generated.Config{
Resolvers: &graph.Resolver{
UserSubscribers: make(map[string]chan *model.UserUpdated),
UserStore: store.NewUserStore(),
},
},
),
)
srv.AddTransport(transport.Websocket{
KeepAlivePingInterval: time.Second * 10,
Upgrader: websocket.Upgrader{
CheckOrigin: func(r *http.Request) bool {
return true
},
},
})
Now you can implement GraphQL handler function in the graph/schema.resolver.go
file
func (r *mutationResolver) CreateUser(ctx context.Context, input model.CreateUserInput) (*model.User, error) {
user := r.UserStore.Save(input.Name, input.Age)
r.mu.Lock()
for _, v := range r.UserSubscribers {
v <- &model.UserUpdated{
Info: "created",
User: user,
}
}
r.mu.Unlock()
return user, nil
}
func (r *mutationResolver) DeleteUser(ctx context.Context, id string) (*model.User, error) {
user, err := r.UserStore.Delete(id)
if err != nil {
return nil, err
}
r.mu.Lock()
for _, v := range r.UserSubscribers {
v <- &model.UserUpdated{
Info: "deleted",
User: user,
}
}
r.mu.Unlock()
return user, nil
}
func (r *queryResolver) Users(ctx context.Context) ([]*model.User, error) {
return r.UserStore.GetAllUsers(), nil
}
func (r *queryResolver) User(ctx context.Context, id string) (*model.User, error) {
return r.UserStore.FindByID(id)
}
func (r *subscriptionResolver) UserUpdated(ctx context.Context) (<-chan *model.UserUpdated, error) {
subscriberID := uuid.New().String()
ch := make(chan *model.UserUpdated, 1)
r.mu.Lock()
r.UserSubscribers[subscriberID] = ch
r.mu.Unlock()
go func() {
<-ctx.Done()
r.mu.Lock()
delete(r.UserSubscribers, subscriberID)
r.mu.Unlock()
}()
return ch, nil
}
Finally, you can start the GraphQL API server
go run .
visit the GraphQL playground at localhost:8080
and test all queries we have implemented
mutation {
createUser(input: {name: "albert", age: 13}) {
id
name
}
}
query {
users {
id
name
age
}
}
query {
user(id: "xxxxxxxxxxx") {
name
age
}
}
mutation {
deleteUser(id: "xxxxxxxxxxx") {
name
age
}
}
For the subscription function, open another tab for the playground and do this query
subscription {
userUpdated {
info
user {
id
name
age
}
}
}
Try to create or delete a user from the other tab to see what will happen.
To use this GraphQL with other frontend application, by default you will need to connect to localhost:8080/query
.
You might use Apollo library for JS to make API requests from your website application. Thank you for reading...
sources