98 Membuat Plugin Sendiri untuk graphql-go
Membangun aplikasi modern yang scalable dan maintainable tidak terlepas dari pemilihan arsitektur API yang tepat. GraphQL, dengan segala kelebihannya dibandingkan REST, telah menjadi primadona dalam ekosistem development saat ini. Salah satu library Go yang sangat populer untuk mengimplementasikan GraphQL adalah graphql-go
. Namun, tahukah Anda bahwa Anda dapat memperluas kemampuannya dengan membuat plugin sendiri?
Di artikel ini, saya akan membahas praktik membuat plugin custom pada library graphql-go
. Tidak hanya teori, saya juga akan berikan contoh kode, diagram alur, dan simulasi penggunaan plugin yang Anda buat sendiri. Ini penting jika Anda ingin mengintegrasikan logika lintas bidang aplikasi seperti logging, validasi, atau bahkan authentication secara modular.
Kenapa Membutuhkan Plugin di graphql-go?
Meskipun graphql-go
sendiri tidak memakai sistem plugin
resmi seperti pada TypeScript atau Python, pada praktiknya kita bisa membuat middleware, extension, atau bahkan meng-inject hook ke dalam proses request GraphQL. Pattern ini biasa disebut “plugin-like behavior” atau “extension points”.
Contoh use-case:
Kebutuhan | Kenapa Plugin? |
---|---|
Logging query | Audit, trace, debugging lebih mudah |
Validasi custom | Validasi di luar skema GraphQL (misal validasi business) |
Rate Limiting | Memproteksi endpoint dari abuse |
Custom Authentication | Integrasi SSO, JWT, token, dst |
Konsep: Dimana Plugin Bisa Ditempatkan?
Untuk itu, kita perlu paham siklus hidup penanganan request pada graphql-go
.
Sederhananya:
- HTTP Handler menerima request GraphQL.
- Handler men-decode dan mem-parsing query.
- Query dieksekusi pada root resolver.
- Hasilnya di-encode ke JSON, dikirim ke client.
Kita bisa menyuntikan extensibility paling fleksibel pada HTTP Handler atau setiap resolver. Berikut adalah diagram siklus hidup request dengan “hook” area untuk plugin:
flowchart DK A[HTTP Handler Menerima Request] --> B{Plugin Middleware} B --> C[Decode / Parse Query] C --> D{Plugin Eksekusi Pre-Resolver} D --> E[Eksekusi Resolver] E --> F{Plugin Eksekusi Post-Resolver} F --> G[Encode Response] G --> H[Return ke Client]
Plugin bisa dipasang di middleware handler, sebelum/ sesudah resolver, atau bahkan di dalam resolver.
Studi Kasus: Membuat Plugin Logging Query GraphQL
Mari kita mulai dengan use case sederhana: logging seluruh query dan hasil eksekusinya.
Step 1: Persiapan Project
Buat project Go baru, install graphql-go:
go mod init my-graphql-plugin
go get github.com/graph-gophers/graphql-go
go get github.com/graph-gophers/graphql-go/relay
Siapkan skema sederhana:
# schema.graphql
type Query {
hello(name: String!): String!
}
Siapkan resolver dasar:
// resolver.go
package main
type Resolver struct{}
func (r *Resolver) Hello(args struct{ Name string }) string {
return "Hello, " + args.Name
}
Step 2: Membuat Plugin Logging
Buat struct plugin, yang nantinya akan membungkus handler GraphQL milik relay
.
Interface Plugin (Pattern Middleware)
Kita define plugin sebagai fungsi yang menerima http.Handler dan mengembalikan http.Handler lagi:
// plugin.go
package main
import "net/http"
type Plugin func(http.Handler) http.Handler
Implementasi Logging Plugin
// logging_plugin.go
package main
import (
"bytes"
"io"
"io/ioutil"
"log"
"net/http"
)
func LoggingPlugin(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.Method == http.MethodPost {
// Read the body (may be io.ReadCloser)
var buf bytes.Buffer
tee := io.TeeReader(r.Body, &buf)
body, _ := ioutil.ReadAll(tee)
r.Body.Close()
r.Body = io.NopCloser(&buf)
// Log query GraphQL
log.Printf("[GraphQL-QUERY]: %s", string(body))
}
// Eksekusi handler berikutnya (relay)
next.ServeHTTP(w, r)
})
}
Membungkus Handler Relay Dengan Plugin
// main.go
package main
import (
"log"
"net/http"
"os"
"github.com/graph-gophers/graphql-go"
"github.com/graph-gophers/graphql-go/relay"
)
func main() {
schemaBytes, err := os.ReadFile("schema.graphql")
if err != nil {
log.Fatal(err)
}
schema := graphql.MustParseSchema(string(schemaBytes), &Resolver{})
relayHandler := &relay.Handler{Schema: schema}
// Bungkus handler dengan plugin
handlerWithPlugin := LoggingPlugin(relayHandler)
http.Handle("/graphql", handlerWithPlugin)
log.Println("Server run at :8080")
log.Fatal(http.ListenAndServe(":8080", nil))
}
Sekarang, setiap request GraphQL yang masuk akan terekam log-nya:
$ curl -XPOST -d '{"query":"{ hello(name:\"Budi\") }"}' localhost:8080/graphql
Log pada server:
[GraphQL-QUERY]: {"query":"{ hello(name:\"Budi\") }"}
Step 3: Mendukung Multiple Plugin Sekaligus
Bagaimana jika ingin chaining banyak plugin sekaligus, misal logging, rate-limiting dan authentication?
Define fungsi untuk chaining plugin:
// plugin_chain.go
package main
import "net/http"
func ChainPlugins(handler http.Handler, plugins ...Plugin) http.Handler {
for _, plugin := range plugins {
handler = plugin(handler)
}
return handler
}
Penggunaannya:
handlerWithPlugins := ChainPlugins(relayHandler,
LoggingPlugin,
RateLimitPlugin,
AuthPlugin,
)
http.Handle("/graphql", handlerWithPlugins)
Step 4: Simulasi: Membuat Plugin Rate Limiting Sederhana
Sebagai ilustrasi, inilah contoh plugin sederhana untuk membatasi 5 requests per IP tiap menit:
// rate_limit_plugin.go
package main
import (
"net/http"
"sync"
"time"
)
type rateLimiter struct {
mu sync.Mutex
store map[string][]time.Time
}
func NewRateLimiter() *rateLimiter {
return &rateLimiter{
store: make(map[string][]time.Time),
}
}
func (rl *rateLimiter) Middleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
ip := r.RemoteAddr
rl.mu.Lock()
reqs := rl.store[ip]
now := time.Now()
// Remove expired timestamps (older than 1 min)
newReqs := []time.Time{}
for _, t := range reqs {
if now.Sub(t) < time.Minute {
newReqs = append(newReqs, t)
}
}
if len(newReqs) >= 5 {
rl.mu.Unlock()
http.Error(w, "Rate limit exceeded", http.StatusTooManyRequests)
return
}
rl.store[ip] = append(newReqs, now)
rl.mu.Unlock()
next.ServeHTTP(w, r)
})
}
Gabungkan ke ChainPlugins
:
limiter := NewRateLimiter()
handlerWithPlugins := ChainPlugins(relayHandler,
LoggingPlugin,
limiter.Middleware,
)
Step 5: Advanced—Plugin per-Resolver
Untuk akses ke context yang lebih dalam, Anda bisa membuat wrapper pada resolver, misal untuk logging argumen setiap resolver. Gunakan interface dekorator, meskipun pattern ini lebih advanced dan verbose. Opsi lain: inject dependency ke resolver struct.
Kesimpulan
Dengan pattern plugin-like middleware di graphql-go
, Anda bisa memperkaya server GraphQL Go Anda dengan fitur middleware modern ala ExpressJS atau FastAPI—meski Go sendiri tidak punya sintaks plugin
natif. Dengan menerapkan pola ini, logging, authentication, hingga rate limiting bisa Anda kelola secara modular, testable, dan scalable.
Melangkah lebih jauh, Anda bisa membuat plugin open source sendiri, mendistribusikan ke tim, atau bahkan komunitas!
Rangkuman
- Plugin di Go = middleware pattern, diterapkan pada handler HTTP.
- Bisa chain beberapa plugin sekaligus.
- Digunakan untuk: logging, auth, monitoring, rate limiting, dsb.
- Mudah diintegrasikan ke project berbasis GraphQL-go.
- Membantu menjaga codebase tetap clean dan modular.
Jangan ragu bereksperimen dengan middleware custom Anda sendiri—karena sebuah arsitektur yang tangguh adalah arsitektur yang mudah diperluas! 🚀
97 GraphQL Code Generator dan Otomatisasi Skema
Artikel Terhangat
99 Kontribusi ke Open Source graphql-go
10 Oct 2025
98 Membuat Plugin Sendiri untuk graphql-go
10 Oct 2025
96 Migrasi REST API ke GraphQL
10 Oct 2025
94 Studi Kasus: Dashboard Admin Real-time
10 Oct 2025

99 Kontribusi ke Open Source graphql-go

98 Membuat Plugin Sendiri untuk graphql-go

96 Migrasi REST API ke GraphQL
