Skip to content
Santekno.com | Level Up Your Engineering Skills
ID
📖 0%
30 Jul 2025 · 6 mnt baca ·Artikel 30 / 125
Go

30 Validasi Input di Mutation graphql-go

IH
Ihsan Arif
Penulis di Santekno · Backend Engineer

Pada era perkembangan aplikasi modern yang serba cepat dan terhubung, GraphQL telah menjadi pilihan utama dalam membangun API karena fleksibilitas dan efisiensinya. Namun, sering kali developer terlalu fokus pada desain schema dan resolver, lalu agak lalai pada aspek penting lainnya: validasi input, terutama pada operasi mutation.

Artikel ini akan membahas bagaimana menambahkan dan mengelola 30 validasi input pada mutation menggunakan library graphql-go di bahasa pemrograman Go. Kita juga akan membahas contoh kode, simulasi, tabel validasi, dan blueprint arsitektur penyusunan validasi secara clean.


Mengapa Perlu Validasi di Layer Mutation?

Validasi input adalah proses memastikan data yang diterima oleh API telah sesuai dengan aturan, logika bisnis, maupun tipe data yang diharapkan. Dalam context mutation GraphQL, validasi input menjadi pintu gerbang agar data corrupt tidak menembus lebih dalam ke database, serta menjaga integrity sistem.

Tanpa validasi yang baik, masalah seperti data inconsistency, runtime error hingga security vulnerability seringkali muncul.


Sekilas Tentang graphql-go

graphql-go adalah salah satu library populer di Go untuk membangun GraphQL server. Di sini, developer mendefinisikan schema dan resolver, namun urusan validasi tetap menjadi tanggung jawab kita.

Sebagai ilustrasi, definisi mutation sederhana di graphql-go:

go
 1var createUserMutation = &graphql.Field{
 2	Type: userType,
 3	Args: graphql.FieldConfigArgument{
 4		"name":    &graphql.ArgumentConfig{Type: graphql.NewNonNull(graphql.String)},
 5		"email":   &graphql.ArgumentConfig{Type: graphql.NewNonNull(graphql.String)},
 6		"age":     &graphql.ArgumentConfig{Type: graphql.Int},
 7		"website": &graphql.ArgumentConfig{Type: graphql.String},
 8	},
 9	Resolve: createUserResolver,
10}

Namun, validasi input tidak otomatis dilakukan library ini—semua tetap di-resolver.


30 Jenis Validasi Input untuk Mutation

Berikut adalah daftar 30 validasi input umum yang sering dan penting diterapkan pada mutation GraphQL:

NoJenis ValidasiContoh Aturan
1Required / Tidak Boleh Kosongname, email wajib diisi
2Panjang MinimumPassword min. 8 karakter
3Panjang MaksimumUsername max. 30 karakter
4Format EmailValidasi email RFC5322
5Format URLValidasi website URL
6Regex CustomNomor telepon sesuai regex
7Nilai UnikEmail tidak boleh duplikat
8Numeric OnlyHanya angka (kode pos, dsb)
9Range AngkaAge: antara 18 dan 75
10Nilai Pilihan EnumStatus: ‘active’, ‘inactive’
11Tanggal ValidFormat ‘YYYY-MM-DD’
12Tidak Boleh NegatifNominal, jumlah
13Nilai DefaultJika field tidak dikirim
14Field Nested Wajibdata.profile.address wajib
15Nested Array ValidateMinimal 1 item, dsb
16Format UUIDid: uuid4 valid
17No Special CharacterValidasi username
18KondisionalJika field A ada, field B wajib
19Array Length Min/MaxMinimal/maksimal item array
20CAPTCHA/Code ValidValidasi kode eksternal
21Required WithField A wajib kalau field B ada
22JSON Structure FormatNested JSON sesuai schema
23Nested Object ValidateValidasi setiap object dalam array
24Existence in DBid user harus exist di DB
25File Upload TypeHanya .jpg, .png
26File Upload SizeMaksimal 2MB
27RELATION ConstraintForeign key exist
28Unique Combinationusername+email kombinasi unik
29List Contains ValueHarus mengandung item X
30Field Tidak Boleh Nilai DefaultTidak boleh ’test’, ‘dummy’

Pengelolaan Validasi: Clean dan Maintainable

Agar validasi 30 aturan di atas tidak menyebabkan resolver penuh if-else dan nested code, pattern clean architecture sangat diperlukan.

Saya biasanya membuat satu package khusus, contoh: validation, lalu setiap validasi dijadikan fungsi reusable.

go
 1package validation
 2
 3import (
 4	"errors"
 5	"regexp"
 6)
 7
 8func RequiredString(s, field string) error {
 9	if s == "" {
10		return errors.New(field + " wajib diisi")
11	}
12	return nil
13}
14
15func ValidateEmail(email string) error {
16	regex := `^[a-z0-9._%+\-]+@[a-z0-9.\-]+\.[a-z]{2,}$`
17	if !regexp.MustCompile(regex).MatchString(email) {
18		return errors.New("Email tidak valid")
19	}
20	return nil
21}

Kemudian di resolver, validasi di-compose:

go
 1func createUserResolver(p graphql.ResolveParams) (interface{}, error) {
 2	name, _ := p.Args["name"].(string)
 3	email, _ := p.Args["email"].(string)
 4	age, _ := p.Args["age"].(int)
 5	website, _ := p.Args["website"].(string)
 6
 7	// 1. Required
 8	if err := validation.RequiredString(name, "name"); err != nil {
 9		return nil, err
10	}
11	// 2. Email format
12	if err := validation.ValidateEmail(email); err != nil {
13		return nil, err
14	}
15	// 3. Age range
16	if age < 18 || age > 75 {
17		return nil, errors.New("Umur harus 18-75 tahun")
18	}
19	// 4. Website format
20	if website != "" {
21		if err := validation.ValidateURL(website); err != nil {
22			return nil, err
23		}
24	}
25	// ... dan seterusnya (hingga 30 validasi)
26	return User{...}, nil
27}

Simulasi: Satu Payload, Banyak Validasi

Kalau aplikasi besar, satu mutation bisa mengandung beberapa level validasi. Simulasi payload di bawah ini akan menunjukkan validasi di berbagai level.

Contoh Payload:

json
 1{
 2  "name": "John Doe",
 3  "email": "john_doe@email.com",
 4  "age": 17,
 5  "website": "not-a-url",
 6  "roles": ["admin", "super_user"],
 7  "profile": {
 8    "bio": "",
 9    "address": "Jl. Sudirman"
10  }
11}

Layer Validasi

  • name: Required, length max 50
  • email: Required, email format, unique in DB
  • age: Required, range 18-65
  • website: Optional, jika ada harus URL yang valid
  • roles: Array min 1, harus enum valid (‘admin’, ‘user’, ’editor’)
  • profile.bio: Panjang max 160
  • profile.address: Required

Diagram Alur Validasi (Kode Mermaid)

MERMAID
flowchart TD
    Start --> CekName[Validasi name]
    CekName -- OK --> CekEmail[Validasi email]
    CekEmail -- OK --> CekAge[Validasi age]
    CekAge -- OK --> CekWebsite[Validasi website]
    CekWebsite -- OK --> CekRoles[Validasi roles]
    CekRoles -- OK --> CekProfileBio[Validasi profile.bio]
    CekProfileBio -- OK --> CekProfileAddress[Validasi profile.address]
    CekProfileAddress -- OK --> Sukses
    CekName -- ERROR --> Error["Return error"]
    CekEmail -- ERROR --> Error
    CekAge -- ERROR --> Error
    CekWebsite -- ERROR --> Error
    CekRoles -- ERROR --> Error
    CekProfileBio -- ERROR --> Error
    CekProfileAddress -- ERROR --> Error


Pattern: Validasi Aggregator

Agar setiap error tersebar, pattern error aggregator sangat membantu. Kita bisa menyimpan semua pesan error validasi, lalu di akhir hanya return jika error tidak kosong.

go
 1var validationErrs []string
 2
 3if err := validation.RequiredString(name, "name"); err != nil {
 4	validationErrs = append(validationErrs, err.Error())
 5}
 6// ... semua validasi
 7
 8if len(validationErrs) > 0 {
 9	return nil, errors.New(strings.Join(validationErrs, "; "))
10}

Testing: Simulasikan Request

Untuk memastikan validasi bekerja, buatlah test dengan table driven test untuk setiap aturan.

go
 1func TestValidateCreateUserInput(t *testing.T) {
 2	tests := []struct{
 3		payload  map[string]interface{}
 4		wantErr  bool
 5	}{
 6		{
 7			payload: map[string]interface{}{"name": "", "email": "salah@", "age": 10},
 8			wantErr: true,
 9		},
10		{
11			payload: map[string]interface{}{"name": "Budi", "email": "budi@mail.com", "age": 20},
12			wantErr: false,
13		},
14		// dst, sesuai checklist 30 validasi
15	}
16}

Kesimpulan

Menanamkan 30 validasi input mutation di graphql-go—dan scalable validasi untuk puluhan field—menuntut disiplin arsitektur clean serta kreatifitas dalam compose fungsi validasi. Validasi bukan hanya tegas pada sisi data type, namun juga pada custom rule, kondisional, maupun relasi DB.

Dengan pendekatan reusable validation function, error aggregator, dan error yang user-friendly, keamanan serta reliability API akan jauh meningkat.

Penutup:
Jangan anggap enteng validasi input mutation. Investasi waktu di awal akan menghindarkan deretan bug, serangan, hingga downtime di masa depan.


Referensi


Semoga artikel ini membantumu, dan—seperti biasa—jaga codebase-mu tetap robust dan clean! 🚀

Artikel Terkait

💬 Komentar