/gqlgen-sqlboiler

This is a plugin for gqlgen to generate converts + filter queries and resolvers for sqlboiler

Primary LanguageGoMIT LicenseMIT

gqlgen-sqlboiler

We want developers to be able to build software faster using modern tools like GraphQL, Golang, React Native without depending on commercial providers like Firebase or AWS Amplify.

This program generates type-safe code between your generated gqlgen and sqlboiler models with support for unique id's across your whole database (also if you're not using string id's). We can automatically generate the implementation of queries and mutations like create, update, delete based on your graphql scheme and your sqlboiler models.

To make this program a success coupling (same naming) between your database and graphql scheme is needed at the moment. The advantage of this program is the most when you have a database already designed. However everything is created with support for change so you could write some extra GrapQL resolvers if you'd like without a problem.

Why gqlgen and sqlboiler

They go back to a schema first approach which we like. The generated code with these tools are the most efficient and fast in the Golang system (and probably outside of it too).

It's really amazing how fast a generated api with these techniques is!

Usage

  1. Generate database structs with: https://github.com/volatiletech/sqlboiler (--no-back-referencing is IMPORTANT!) e.g. sqlboiler mysql --no-back-referencing
  2. (optional, but recommended) Generate GrapQL scheme from sqlboiler structs: https://github.com/web-ridge/sqlboiler-graphql-schema
    e.g. go run github.com/web-ridge/sqlboiler-graphql-schema --output=schema.graphql --skip-input-fields=userId --directives=isAuthenticated --pagination=no
  3. Install: https://github.com/99designs/gqlgen
  4. Generate gqlgen structs + converts between gqlgen and sqlboiler with this program
    e.g. go run convert_plugin.go for file contents of that program see bottom of this readme

Done

  • Generate converts between sqlboiler structs and graphql (with relations included)
  • Generate converts between input models and sqlboiler
  • Genarated code understands the difference between empty and null for update inputs so you can set things empty if you explicicitly set them in your mutation!
  • Fetch sqlboiler preloads from graphql context
  • Support for foreign keys named differently than their corresponding model
  • New plugin which generates CRUD resolvers based on mutations in graphql scheme.
  • Support one-to-one relationships inside input types.
  • Generate code which implements the generated where and search filters
  • Batch update/delete generation in resolvers (Not tested yet).
  • Enum support.
  • public errors in resolvers + logging via zerolog. (feel free for PR for configurable logging!)

Roadmap

Requirements

  • Use unsigned ints for foreign keys + ids. Otherwise converts will give compile errors. Unsigned ints for id's is allso recommended since it gives you twice as big id's and id's should not be negative anyway ;)

Examples

https://github.com/web-ridge/gqlgen-sqlboiler-examples

More examples are welcome. Send a PR ;-)

Example result of this plugin

https://github.com/web-ridge/gqlgen-sqlboiler-examples/tree/master/social-network/helpers

Code snippet

func PostToGraphQL(m *models.Post) *graphql_models.Post {
	if m == nil {
		return nil
	}

	r := &graphql_models.Post{
		ID:      PostIDToGraphQL(m.ID),
		Content: m.Content,
	}

	if boilergql.UintIsFilled(m.UserID) {
		if m.R != nil && m.R.User != nil {
			r.User = UserToGraphQL(m.R.User)
		} else {
			r.User = UserWithUintID(m.UserID)
		}
	}
	if m.R != nil && m.R.Comments != nil {
		r.Comments = CommentsToGraphQL(m.R.Comments)
	}
	if m.R != nil && m.R.Images != nil {
		r.Images = ImagesToGraphQL(m.R.Images)
	}
	if m.R != nil && m.R.Likes != nil {
		r.Likes = LikesToGraphQL(m.R.Likes)
	}

	return r
}

sqlboiler.yml

mysql:
  dbname: dbname
  host: localhost
  port: 8889
  user: root
  pass: root
  sslmode: "false"
  blacklist:
    - notifications
    - jobs
    - password_resets
    - migrations
mysqldump:
  column-statistics: 0

gqlgen.yml

schema:
  - schema.graphql
exec:
  filename: graphql_models/generated.go
  package: graphql_models
model:
  filename: graphql_models/genereated_models.go
  package: graphql_models
resolver:
  filename: resolver.go
  type: Resolver

Run normal generator go run github.com/99designs/gqlgen -v

Put this in your program convert_plugin.go e.g.

// +build ignore

package main

import (
	"fmt"
	"os"

	"github.com/99designs/gqlgen/api"
	"github.com/99designs/gqlgen/codegen/config"
	gbgen "github.com/web-ridge/gqlgen-sqlboiler/v2"
)

func main() {
	cfg, err := config.LoadConfigFromDefaultLocations()
	if err != nil {
		fmt.Fprintln(os.Stderr, "failed to load config", err.Error())
		os.Exit(2)
	}

	output := gbgen.Config{
		Directory:   "helpers", // supports root or sub directories
		PackageName: "helpers",
	}
	backend := gbgen.Config{
		Directory:   "models",
		PackageName: "models",
	}
	frontend := gbgen.Config{
		Directory:   "graphql_models",
		PackageName: "graphql_models",
	}

	err = api.Generate(cfg,
		api.AddPlugin(gbgen.NewConvertPlugin(
			output,   // directory where convert.go, convert_input.go and preload.go should live
			backend,  // directory where sqlboiler files are put
			frontend, // directory where gqlgen models live
		)),
		api.AddPlugin(gbgen.NewResolverPlugin(
			output,
			backend,
			frontend,
			"github.com/web-ridge/yourapp/yourauth", // leave empty if you don't have auth
		)),
	)
	if err != nil {
		fmt.Println("error!!")
		fmt.Fprintln(os.Stderr, err.Error())
		os.Exit(3)
	}
}

go run convert_plugin.go

Help us

We're the most happy with your time investments and/or pull request to improve this plugin. Feedback is also highly appreciated.

If you don't have time or knowledge to contribute and we did save you a lot of time, please consider a donation so we can invest more time in this library: paypal