32 Setting Up an ORM (gorm) in graphql-go
32 Setting Up an ORM (gorm) in graphql-go: An Integrated Guide for Backend Engineers
For backend engineers in the Go (Golang) ecosystem, building a robust API often requires integrating a database, an ORM, and a GraphQL schema. Two popular tools in the Go world, gorm as the ORM and graphql-go as the GraphQL server, are the go-to choice for many teams. This article takes an in-depth look at setting up gorm in a graphql-go project, complete with code examples, simulations, flow diagrams, and tips from seasoned engineers.
🛠 Preparation: Installing Dependencies
Before writing any code, make sure the following packages are installed:
1go get github.com/jinzhu/gorm
2go get github.com/go-sql-driver/mysql
3go get github.com/graph-gophers/graphql-go
4go get github.com/graph-gophers/graphql-go/relayWe’ll be using MySQL in this example. Feel free to swap in another driver as needed (postgres, sqlite, etc.).
👩💻 Schema & Requirements for the Project
We’ll simulate a simple scenario:
Use Case: Managing a list of users.
- Each user has: id, name, email.
Database Schema
1CREATE TABLE users (
2 id INT AUTO_INCREMENT PRIMARY KEY,
3 name VARCHAR(100),
4 email VARCHAR(100) UNIQUE
5);GraphQL Schema
1type User {
2 id: ID!
3 name: String!
4 email: String!
5}
6
7type Query {
8 users: [User!]!
9 user(id: ID!): User
10}
11type Mutation {
12 createUser(name: String!, email: String!): User
13}👨🔬 Integrating gorm into graphql-go, Step by Step
Let’s break it down!
1. The GORM Model for User
1// models/user.go
2package models
3
4import "github.com/jinzhu/gorm"
5
6type User struct {
7 gorm.Model
8 Name string `gorm:"type:varchar(100)"`
9 Email string `gorm:"type:varchar(100);unique"`
10}2. Setting Up the Database & Auto Migrate
1// db/db.go
2package db
3
4import (
5 "github.com/jinzhu/gorm"
6 _ "github.com/go-sql-driver/mysql"
7 "log"
8 "user-gql/models"
9)
10
11func InitDB() *gorm.DB {
12 db, err := gorm.Open("mysql", "root:password@/userdb?charset=utf8&parseTime=True")
13 if err != nil {
14 log.Fatal(err)
15 }
16 db.AutoMigrate(&models.User{})
17 return db
18}3. The GraphQL Resolver
A resolver is the ‘bridge’ between a GraphQL query/mutation and your Go code (including the ORM).
1// resolver/resolver.go
2package resolver
3
4import (
5 "context"
6 "user-gql/models"
7 "github.com/jinzhu/gorm"
8)
9
10type Resolver struct {
11 DB *gorm.DB
12}
13
14// For the users query
15func (r *Resolver) Users(ctx context.Context) ([]*UserResolver, error) {
16 var users []models.User
17 if err := r.DB.Find(&users).Error; err != nil {
18 return nil, err
19 }
20 resolvers := make([]*UserResolver, len(users))
21 for i, user := range users {
22 resolvers[i] = &UserResolver{user}
23 }
24 return resolvers, nil
25}
26
27// Single query by ID
28func (r *Resolver) User(ctx context.Context, args struct{ ID int32 }) (*UserResolver, error) {
29 var user models.User
30 if err := r.DB.First(&user, args.ID).Error; err != nil {
31 return nil, err
32 }
33 return &UserResolver{user}, nil
34}
35
36// Mutation
37func (r *Resolver) CreateUser(ctx context.Context, args struct{ Name string; Email string }) (*UserResolver, error) {
38 user := models.User{Name: args.Name, Email: args.Email}
39 if err := r.DB.Create(&user).Error; err != nil {
40 return nil, err
41 }
42 return &UserResolver{user}, nil
43}
44
45// Resolver for the User type
46type UserResolver struct {
47 models.User
48}
49
50func (u *UserResolver) ID() graphql.ID { return graphql.ID(fmt.Sprint(u.User.ID)) }
51func (u *UserResolver) Name() string { return u.User.Name }
52func (u *UserResolver) Email() string { return u.User.Email }4. The GraphQL Schema
Create a schema.graphql file with the following content:
1type User {
2 id: ID!
3 name: String!
4 email: String!
5}
6
7type Query {
8 users: [User!]!
9 user(id: ID!): User
10}
11type Mutation {
12 createUser(name: String!, email: String!): User
13}5. Integrating the HTTP Handler
1// main.go
2package main
3
4import (
5 "io/ioutil"
6 "net/http"
7 "user-gql/db"
8 "user-gql/resolver"
9
10 "github.com/graph-gophers/graphql-go"
11 "github.com/graph-gophers/graphql-go/relay"
12)
13
14func main() {
15 // Setup DB
16 database := db.InitDB()
17 defer database.Close()
18
19 // Load schema
20 schemaBytes, err := ioutil.ReadFile("schema.graphql")
21 if err != nil {
22 panic(err)
23 }
24
25 // Build the GraphQL schema
26 schema := graphql.MustParseSchema(
27 string(schemaBytes),
28 &resolver.Resolver{DB: database},
29 )
30
31 http.Handle("/query", &relay.Handler{Schema: schema})
32
33 // Live!
34 http.ListenAndServe(":8080", nil)
35}🏃 Simulating Queries Against the Server
1. Query All Users
1query {
2 users {
3 id
4 name
5 email
6 }
7}Response:
1{
2 "data": {
3 "users": [
4 {"id": "1", "name": "Ridwan", "email": "ridwan@contoh.com"},
5 {"id": "2", "name": "Rahma", "email": "rahma@company.com"}
6 ]
7 }
8}2. Mutation: Add a User
1mutation {
2 createUser(name: "Bimo", email: "bimo@team.com") {
3 id
4 name
5 }
6}📊 Table: GORM vs Raw SQL Comparison
| Feature | GORM | Raw SQL |
|---|---|---|
| Query Builder | YES (ORM style) | NO |
| Auto Migration | YES | NO |
| Relations | YES (Preload, Joins) | Manual (Join) |
| Struct Mapping | YES (Struct to Table) | Handcrafted |
| Error Handling | Mid (has an error type) | Manual/built-in errors |
| Readability | Cleaner & more idiomatic | Sometimes verbose |
🔖 Setup Flow Diagram
flowchart TD
A[Inisiasi: Load Schema GraphQL] --> B[Init GORM DB]
B --> C[Bind Resolver ke GORM DB]
C --> D[GraphQL HTTP Handler aktif]
D --> E[Receive HTTP Request /query]
E --> F{Jenis Query}
F -- Query user/users --> G[GORM query ke DB]
F -- Mutation --> H[GORM transaksi ke DB]
G & H --> I[Json Response via GraphQL]
⚡️ Engineering Tips
- Connection Pool: Use
db.DB().SetMaxOpenConns(...)to optimize performance. - Env Config: Store database credentials in environment variables, not hardcoded.
- Validation: Validate input in mutations before committing to the DB (don’t trust the client too much!).
- Migration: Run migrations separately in production, not from the main app.
- Testing: Use a test DB for integration tests, and mock GORM with a library such as go-sqlmock .
💬 Wrapping Up
With the setup above, you can connect the GORM ORM and graphql-go in a clean, tidy, and maintainable way. The key: always treat the resolver as a clean layer that connects the GraphQL world with your ORM/database.
Don’t hesitate to adapt this pattern to the needs of your own project, whether for e-commerce, SaaS, or enterprise scale. If you have any questions or need further explanation, drop them in the comments 😉
References:
Happy coding, backend warriors! 🚀