• Stars
    star
    150
  • Rank 247,323 (Top 5 %)
  • Language
    Go
  • License
    MIT License
  • Created almost 5 years ago
  • Updated over 4 years ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

This a simple gqlgen repository example which includes basic models and interactions with a pg database

Application Logo

GQLGen PG TODO Example

A simple, no fuss, example thats updated regularly to stay current with the API landscape

Introduction

This project is intended to help newcomers to gqlgen and GraphQL. This isn't meant to be the ONLY way you should organize and build your application. I took extensively liberties here to minimize func calls and writing helper wrappers which do nothing more than confuse newcomers.

Setup

  1. Install Go 1.13 or greater.
    • The recommended approach is to use the installer to get started.
  2. Install Postgres
    • This varies depending on the environment that you're on and I don't intend for this repository to list the steps necessary to install pg.
  3. Ensure that you have a todos database created
    • There's a makefile step already available, make init which will create the database if it doesn't already exist
  4. Start the magic by running the following command
    • go run main.go

Updating GraphQL Models + CodeGen

The application uses (GQLGEN) to generate statically typed bindings for all of the grahql models.

  1. Generate the Go model that represents the graph model. An example looks like the following:
package models

import (
	"time"
)

// User Graph datamodel
type User struct {
	ID     string `json:"id"`
	Email  string `json:"email"`

	FirstName   string `json:"firstName"`
	LastName    string `json:"lastName"`

	CreatedAt     time.Time `json:"createdAt"`
	UpdatedAt     time.Time `json:"updatedAt"`
}
  1. Add/Update your model in the /schema folder of the application. An example looks like the following. Note that you must reference the graph model package with the @goModel directive.
type User
  @goModel(
    model: "github.com/oshalygin/gqlgen-pg-todo-example/models/models.User"
  ) {
  id: ID!
  email: String!

  firstName: String!
  lastName: String!

  createdAt: Time!
  updatedAt: Time!
}
  1. Run the codegen process
make gen
  1. Implement the resolver within the resolvers package.

Generating Dataloaders

# Run the following commands to generate dataloaders
# This command will place a codegen dataloader in the graph/generated folder
# Note that the loader argument is required and the value is case sensitive
make dataloader loader=User

You must still create the appropriate dataloader in the codebase to support the generated implementation.

// Create the loader that resembles other loaders in the dataloaders package
// This is merely an example

func User(db *pg.DB, w http.ResponseWriter, r *http.Request, next http.Handler) {
	loader := generated.NewUserLoader(generated.UserLoaderConfig{
		MaxBatch: 100,
		Wait:     1 * time.Millisecond,
		Fetch: func(keys []int) ([]*models.User, []error) {

			var dbUsers []*models.User
			// This query does NOT return an array that matches the order of the IN
			// clause.  Meaning: SELECT * FROM Users where id IN (1,8,3)
			// will not return users 1, 8, 3 in that order.  This order is VERY important
			// as that is how the dataloaden library resolves and matches objects.  Note the ids here are collected via
			// goroutines and the order is not going to be nicely ordered to match your DB
			// result query.  Try adding a breakpoint here and looking at the arg(keys) and the
			// resulting array from the following where query.
			err := db.Model(&dbUsers).WhereIn("id IN (?)", keys).Select()

			if err != nil {
				return []*models.User{}, []error{err}
			}

			// All we're doing here on out is just ordering our
			// collection to match the argument keys []int collection
			userKeys := make(map[int]*models.User)
			users := make([]*models.User, len(keys))

			for _, user := range dbUsers {
				userKeys[user.ID] = user

			}

			for i, k := range keys {
				if user, ok := userKeys[k]; ok {
					users[i] = user
				}
			}

			return users, []error{err}
		},
	})

	ctx := context.WithValue(r.Context(), UserLoader, loader)
	next.ServeHTTP(w, r.WithContext(ctx))
}


// Add a new entry in the NewMiddleware function

func NewMiddleware(session *mgo.Session) []func(handler http.Handler) http.Handler {
	return []func(handler http.Handler) http.Handler{
		setLoader(session, User),
    // setLoader(session, YourLoader)
	}
}

Note if you're going to use the make script for dataloader creation, you may consider extending it to take something other than an int for the key(in the case of uuid keys)

Resolvers

The first step before adding a resolver is to define a new mutation/query or property in your GQL Schema. For example:

todoCreate(todo: TodoInput!): Todo!

Ensure that you actually have a TodoInput input type defined:

input TodoInput
  @goModel(
    model: "github.com/oshalygin/gqlgen-pg-todo-example/models.TodoInput"
  ) {
  name: String!
  createdBy: Int!
}

Run the code gen:

make gen
# or
go generate ./...

Depending on what you added you will now get compiler errors in the file resolvers.go. What you see depends on your terminal output or your IDEA. Here are some examples of unimplemented code:

Unimplemented Resolver

# github.com/oshalygin/gqlgen-pg-todo-example/resolvers
resolvers/resolver.go:14:27: cannot use &mutationResolver literal (type *mutationResolver) as type generated.MutationResolver in return argument:
	*mutationResolver does not implement generated.MutationResolver (missing TodoComplete method)

Sample Queries

query {
  users {
    id
    email
    firstName
    lastName
  }
}
query {
  user(id: 1) {
    id
    email
    firstName
    lastName
  }
}
mutation {
  userCreate(
    user: {
      email: "[email protected]"
      firstName: "Gandalf"
      lastName: "Wizard"
    }
  ) {
    id
    email
    firstName
    lastName
    createdAt
  }
}
query {
  todo(id: 1) {
    id
    name
    isComplete
    isDeleted
    createdAt
    updatedAt
    createdBy {
      firstName
      lastName
    }
  }
}
query {
  todos {
    id
    name
    isComplete
    createdBy {
      firstName
      lastName
      email
    }
  }
}
mutation {
  todoComplete(id: 1, updatedBy: 2) {
    id
    name
    isComplete
    isDeleted

    createdBy {
      firstName
    }

    updatedBy {
      firstName
    }

    createdAt
    updatedAt
  }
}

Dependencies

Tech Description
gqlgen gqlgen is a Go library for building GraphQL servers without any fuss.
color Colorizes text. Who doesn't like colorful stdout?
go-chi This is an http router. Pick your flavor, this is mine.
go-pg This is our fancy Go Postgre ORM. You could write raw SQL queries if you wanted to, but I rather not for this sample project
yacspin A nice and simple terminal spinner used during seed steps

License

MIT

More Repositories

1

git-backup

Binaries to backup your git repositories to BitBucket
Go
13
star
2

k8s-config

A CLI utility that updates Kubernetes configuration files
Go
11
star
3

go-tag

Your dirt simple Git Tagging utility. Stop writing bash scripts today!
Go
8
star
4

ReactReduxRecipes

Application that implements a set of requirements with React, Redux and CI.
JavaScript
4
star
5

go-changelog

Go based git changelog generator
Go
3
star
6

algorithm-solutions

Various Algorithm solutions written in ES6/ES7/ES8 with a FP approach
JavaScript
3
star
7

telegram-stravabot

This is a golang Telegram StravaBot which gives you updates on how many miles you've ridden and if you're on pace
Go
3
star
8

npm-auth

Utility that sets the .npmrc file based on a set of environment variables. Can be used with Travis or any CI environment.
JavaScript
3
star
9

biaja-bookings

A bookings scraping application
JavaScript
2
star
10

ReactReduxStickyNotes

Sticky Note application in React and Redux
JavaScript
2
star
11

SolutionPub

Blog created with .NET 5, TypeScript, AngularJS 1.x and GulpJS
JavaScript
2
star
12

node-dispatcher

A dirt simple node dispatcher, playing around with callbacks and promises - Used for instruction.
JavaScript
2
star
13

FSAESim-Legacy

legacy application of FSAESIM
JavaScript
1
star
14

spike-dynamic-react-forms

Spike to evaluate creation and and consumption of dynamic forms created in the React ecosystem.
JavaScript
1
star
15

go-natsort

A simple scratchpad of a project for natsort
Go
1
star
16

la-county-polling-crawler

A simple and straightforward poll crawling application which will be used as a cloud function
JavaScript
1
star
17

ws-settings-sync

1
star
18

rn-spike

React Native Spike using the albums test proj
JavaScript
1
star
19

telegram-strava-bot

This is a Telegram bot that keeps track of your daily Strava stats
Shell
1
star
20

uclax-js-algowars

Part 2 of the algo war saga...this time with JavaScript
1
star
21

FSAESimRevamp

Going into the next stage with FSAE
JavaScript
1
star
22

olegx-api

The api backend for www.olegx.com
Go
1
star
23

ReactReduxES6Express

A simple application using React with ES6 and Redux. All served up via Express
JavaScript
1
star
24

gogland-settings

These are settings exported from Gogland so that they can be reused with recreated instances
1
star
25

logan-api

The backend API service for logan
Go
1
star
26

product-bot

This is a general purpose bot utility to ping when certain entities on the web are available.
Go
1
star
27

mdjs-io

Merchant Dashboard built in React and Node.js
JavaScript
1
star
28

uclax-algowars

This is a repository to house all of the various Hackerrank solutions for the UCLAX AlgoWars contest
Go
1
star
29

oshalygin

Personal Website www.oshalygin.com
HTML
1
star