【GraphQL&Go】gqlgen basics and MySQL

Basic configuration of graphql

# create go module
$ mkdir gqlgen-todos
$ cd gqlgen-todos
$ go mod init github.com/[username]/gqlgen-todos
# create skeleton project of gqlgen
$ go get github.com/99designs/gqlgen
$ go run github.com/99designs/gqlgen init
├── go.mod
├── go.sum
├── gqlgen.yml // Code generation configuration file
├── graph
│ ├── generated // Automatically generated package
│ │ └── generated.go
│ ├── model // Package for graph model implemented in Go
│ │ └── models_gen.go
│ ├── resolver.go // Root resolver type definition file.
│ ├── schema.graphqls // GraphQL schema definition file.
│ └── schema.resolvers.go // resolver implementation file
└── server.go // Entry point to the app. Not overwritten by regeneration.
# ⭐️ type: Basic object type
type Todo {
id: ID!
text: String!
done: Boolean!
user: User!
}
type User {
id: ID!
name: String!
}
# ⭐️ type Query: Data fetch endpoint definition
type Query {
todos: [Todo!]!
}
# ⭐️ input: Definition of object type for Mutaion
input NewTodo {
text: String!
userId: String!
}
# ⭐️ type Mutation: Endpoint definition of the process to modify the data of Serverside
type Mutation {
createTodo (input: NewTodo!): Todo!
}
$ go run github.com/99designs/gqlgen generate

graph/model/models_gen.go: Go type definition file automatically generated from graph/schema.graphqls

// Code generated by github.com/99designs/gqlgen, DO NOT EDIT.package model// ⭐️ input File generated from New Todo
type NewTodo struct {
Text string `json:" text "`
UserID string `json:"userId "`
}
// File generated from ⭐️ type Todo
type Todo struct {
ID string `json:" id "`
Text string `json:" text "`
Done bool `json:" done "`
User * User `json:" user "`
}
// File generated from ⭐️ type User
type User struct {
ID string `json:" id "`
Name string `json:" name "` `
Friends [] * User `json:" friends "`
}

graph/schema.resolvers.go: Go file for endpoint implementation automatically generated from graph/schema.graphqls file

package graph// This file will be automatically regenerated based on the schema, any resolver implementations
// will be copied through when generating and any unknown code will be moved to the end.
import (
"context"
"fmt"
"math / rand"
"github.com/shkomine/gqlgen-todos/graph/generated"
"github.com/shkomine/gqlgen-todos/graph/model"
)
func (r * mutationResolver) CreateTodo (ctx context.Context, input model.NewTodo) (* model.Todo, error) {
// ⭐️ Implement data fetch here
// For example, this process
// r.todoRepo.Create (input)
panic (fmt.Errorf ("not implemented"))
}
func (r * queryResolver) Todos (ctx context.Context) ([] * model.Todo, error) {
// ⭐️ Implement data changes here
// For example, this process
// r.todoRepo.List ()
panic (fmt.Errorf ("not implemented"))
}
// Mutation returns generated. MutationResolver implementation.
func (r * Resolver) Mutation () generated.MutationResolver {return & mutationResolver {r}}
// Query returns generated.QueryResolver implementation.
func (r * Resolver) Query () generated.QueryResolver {return & queryResolver {r}}
type mutationResolver struct {* Resolver}
type queryResolver struct {* Resolver}
type mutationResolver struct {* Resolver}
type queryResolver struct {* Resolver}

graph/resolver.go, server.go: go package not regenerated by gqlgen command

package graphimport "github.com/shkomine/gqlgen-todos/graph/model"// This file will not be regenerated automatically.
// //
// It serves as dependency injection for your app, add any dependencies you require here.
type Resolver struct {
// ⭐️ TODO: Hold an instance such as UserRepository here
}
package mainimport (
"log"
"net / http"
"os"
"github.com/99designs/gqlgen/graphql/handler"
"github.com/99designs/gqlgen/graphql/playground"
"github.com/shkomine/gqlgen-todos/graph"
"github.com/shkomine/gqlgen-todos/graph/generated"
)
const defaultPort = "8080"func main () {
port: = os.Getenv ("PORT")
if port == "" {
port = defaultPort
}
// ⭐️ Create an instance of Resolver here
srv: = handler.NewDefaultServer (generated.NewExecutableSchema (generated.Config {Resolvers: & graph.Resolver {}}))
// ⭐️ set graphql playground to `/ query`
http.Handle ("/", playground.Handler ("GraphQL playground", "/ query"))
http.Handle ("/ query", srv)
log.Printf ("connect to http: // localhost:% s / for GraphQL playground", port)
log.Fatal (http.ListenAndServe (":" + port, nil))
}

Let’s connect GraphQL to MySQL

# GraphQL schema example
# https://gqlgen.com/getting-started/
scalar Timetype User {
id: ID!
username: String!
email: String!
password: String!
created_at: Time!
is_deleted: Boolean!
}
type Query {
users: [User!]!
}
input NewUser {
username: String!
email: String!
password: String!
}
type Mutation {
createUser(input: NewUser!): User!
}
$ go run github.com/99designs/gqlgen generate
$ go get github.com/go-sql-driver/mysql
package dbimport (
"database/sql"
"fmt"
"os"
_ "github.com/go-sql-driver/mysql"
)
func ConnectDB() (*sql.DB, error) {
USERNAME := os.Getenv("USERNAME")
PASSWORD := os.Getenv("PASSWORD")
HOST := os.Getenv("HOST")
PORT := os.Getenv("PORT")
DATABASE := os.Getenv("DATABASE")
dbconf := USERNAME + ":" + PASSWORD + "@tcp(" + HOST + ":" + PORT + ")/" + DATABASE + "?charset=utf8mb4" + "&parseTime=True" db, err := sql.Open(os.Getenv("DRIVER"), dbconf)
if err != nil {
fmt.Println("Error connecting to database : error=%v", err)
return nil, err
}
return db, err
}
DRIVER=mysql
DSN=user:password@tcp(db-dev:3306)/db_dev
HOST=YOUR_HOST
DATABASE=YOUR_DATABASE
USERNAME=YOUR_NAME
PASSWORD=YOUR_PASSWORD
PORT=YOUR_PORT
$ go get github.com/joho/godotenv
package mainimport (
"log"
"net/http"
"os"
"github.com/99designs/gqlgen/graphql/handler"
"github.com/99designs/gqlgen/graphql/playground"
"github.com/TIshow/learn-to-donate/graph"
"github.com/TIshow/learn-to-donate/graph/generated"
"github.com/joho/godotenv"
)
func envLoad() {
err := godotenv.Load(".env")
if err != nil {
log.Fatalf("Error loading env target")
}
}
const defaultPort = "3306"func main() {
envLoad() // Load from .env file
port := os.Getenv("PORT")
if port == "" {
port = defaultPort
}
srv := handler.NewDefaultServer(generated.NewExecutableSchema(generated.Config{Resolvers: &graph.Resolver{}})) http.Handle("/", playground.Handler("GraphQL playground", "/query"))
http.Handle("/query", srv)
log.Printf("connect to http://localhost:%s/ for GraphQL playground", port)
log.Fatal(http.ListenAndServe(":"+port, nil))
}
package graph// This file will be automatically regenerated based on the schema, any resolver implementations
// will be copied through when generating and any unknown code will be moved to the end.
import (
"context"
"fmt"
"time"
"github.com/TIshow/learn-to-donate/db"
"github.com/TIshow/learn-to-donate/graph/generated"
"github.com/TIshow/learn-to-donate/graph/model"
)
func (r *mutationResolver) CreateUser(ctx context.Context, input model.NewUser) (*model.User, error) {
db, err := db.ConnectDB() // connect to DB.
if err != nil {
panic(err)
} else {
fmt.Println("Successful Connection to DB !")
}
var user model.User
user.Username = input.Username
user.Email = input.Email
user.Password = input.Password
user.IsDeleted = false
now := time.Now()
user.CreatedAt = now
_, err = db.Exec(`INSERT INTO users (username, email, password, created_at, updated_at, is_deleted) VALUES (?, ?, ?, ?, ?, ?)`, user.Username, user.Email, user.Password, user.CreatedAt, user.UpdatedAt, user.IsDeleted)
if err != nil {
panic(err)
} else {
fmt.Println("Insert User is successed !")
}
defer db.Close() return &user, nil
}
func (r *queryResolver) Users(ctx context.Context) ([]*model.User, error) {
return r.users, nil
}
// Mutation returns generated.MutationResolver implementation.
func (r *Resolver) Mutation() generated.MutationResolver { return &mutationResolver{r} }
// Query returns generated.QueryResolver implementation.
func (r *Resolver) Query() generated.QueryResolver { return &queryResolver{r} }
type mutationResolver struct{ *Resolver }
type queryResolver struct{ *Resolver }
db, err := db.ConnectDB() // connect to DB.
var user model.User
user.Username = input.Username
user.Email = input.Email
user.Password = input.Password
user.IsDeleted = false
_, err = db.Exec(`INSERT INTO users (username, email, password, created_at, updated_at, is_deleted) VALUES (?, ?, ?, ?, ?, ?)`, user.Username, user.Email, user.Password, user.CreatedAt, user.UpdatedAt, user.IsDeleted)
mutation {
createUser(input: {username: "test", email: "test", password: "test"}){
id
username
email
password
created_at
is_deleted
}
}

Conclusion

--

--

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store