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

25 Menggunakan Interface dan Union Types di graphql-go

IH
Ihsan Arif
Penulis di Santekno · Backend Engineer

GraphQL memberikan fleksibilitas luar biasa dalam mendefinisikan schema API, terutama saat menangani data yang memiliki banyak bentuk (polymorphic). Di antara fitur powerful tersebut, Interface dan Union Types adalah dua alat utama untuk membangun aplikasi yang scalable dan maintainable. Pada artikel kali ini, kita akan membahas secara komprehensif bagaimana menggunakan Interface dan Union Types di graphql-go, library populer untuk membangun GraphQL server di Go.


Apa Itu Interface dan Union Types di GraphQL?

Sebelum bersentuhan dengan graphql-go, mari kita pahami dulu konsep dasarnya dalam GraphQL.

Interface Type

Interface mirip dengan konsep interface di pemrograman berorientasi objek. Sebuah interface mendefinisikan satu set field yang harus diimplementasikan oleh object type yang mengimplementasinya. Query terhadap interface akan mengembalikan salah satu dari object yang mengimplementasinya.

Contoh sederhana:

graphql
 1interface Character {
 2  id: ID!
 3  name: String!
 4}
 5type Human implements Character {
 6  id: ID!
 7  name: String!
 8  homePlanet: String
 9}
10type Droid implements Character {
11  id: ID!
12  name: String!
13  primaryFunction: String
14}

Union Type

Union mendefinisikan sebuah type yang bisa berupa salah satu dari beberapa type yang ditentukan, tanpa mewajibkan mereka untuk memiliki field yang sama.

Contoh:

graphql
1union SearchResult = Human | Droid | Starship

Mengimplementasikan Interface dan Union di graphql-go

Untuk kebutuhan produksi, kita menggunakan package github.com/graphql-go/graphql .

Skenario: Kita membuat API karakter Star Wars yang memiliki dua jenis karakter (Human dan Droid) serta sebuah fitur pencarian (search) yang hasilnya bisa berupa objek Human, Droid, atau Starship.


1. Instalasi

bash
1go get github.com/graphql-go/graphql

2. Definisi Interface

Mari kita buat interface Character.

go
 1var characterInterface = graphql.NewInterface(graphql.InterfaceConfig{
 2    Name: "Character",
 3    Fields: graphql.Fields{
 4        "id": &graphql.Field{Type: graphql.NewNonNull(graphql.ID)},
 5        "name": &graphql.Field{Type: graphql.NewNonNull(graphql.String)},
 6    },
 7    ResolveType: func(p graphql.ResolveTypeParams) *graphql.Object {
 8        switch p.Value.(type) {
 9        case Human:
10            return humanType
11        case Droid:
12            return droidType
13        default:
14            return nil
15        }
16    },
17})

Penjelasan:

  • ResolveType adalah fungsi penting untuk runtime type resolution. Ia akan menentukan object mana yang dikembalikan ketika client men-query sebuah interface.

3. Definisi Object Type yang Mengimplementasikan Interface

go
 1type Human struct {
 2    ID         string `json:"id"`
 3    Name       string `json:"name"`
 4    HomePlanet string `json:"homePlanet"`
 5}
 6
 7var humanType = graphql.NewObject(graphql.ObjectConfig{
 8    Name:       "Human",
 9    Interfaces: []*graphql.Interface{characterInterface},
10    Fields: graphql.Fields{
11        "id":         &graphql.Field{Type: graphql.NewNonNull(graphql.ID)},
12        "name":       &graphql.Field{Type: graphql.NewNonNull(graphql.String)},
13        "homePlanet": &graphql.Field{Type: graphql.String},
14    },
15})
16
17type Droid struct {
18    ID              string `json:"id"`
19    Name            string `json:"name"`
20    PrimaryFunction string `json:"primaryFunction"`
21}
22
23var droidType = graphql.NewObject(graphql.ObjectConfig{
24    Name:       "Droid",
25    Interfaces: []*graphql.Interface{characterInterface},
26    Fields: graphql.Fields{
27        "id":              &graphql.Field{Type: graphql.NewNonNull(graphql.ID)},
28        "name":            &graphql.Field{Type: graphql.NewNonNull(graphql.String)},
29        "primaryFunction": &graphql.Field{Type: graphql.String},
30    },
31})

4. Definisi Union Type

Misal kita juga punya tipe Starship:

go
 1type Starship struct {
 2    ID    string `json:"id"`
 3    Name  string `json:"name"`
 4    Model string `json:"model"`
 5}
 6
 7var starshipType = graphql.NewObject(graphql.ObjectConfig{
 8    Name: "Starship",
 9    Fields: graphql.Fields{
10        "id":    &graphql.Field{Type: graphql.NewNonNull(graphql.ID)},
11        "name":  &graphql.Field{Type: graphql.NewNonNull(graphql.String)},
12        "model": &graphql.Field{Type: graphql.String},
13    },
14})
15
16var searchResultUnion = graphql.NewUnion(graphql.UnionConfig{
17    Name:  "SearchResult",
18    Types: []*graphql.Object{humanType, droidType, starshipType},
19    ResolveType: func(p graphql.ResolveTypeParams) *graphql.Object {
20        switch p.Value.(type) {
21        case Human:
22            return humanType
23        case Droid:
24            return droidType
25        case Starship:
26            return starshipType
27        default:
28            return nil
29        }
30    },
31})

5. Schema GraphQL dan Query

Mari kita susun root query yang menggunakan Interface dan Union:

go
 1var rootQuery = graphql.NewObject(graphql.ObjectConfig{
 2    Name: "Query",
 3    Fields: graphql.Fields{
 4        "character": &graphql.Field{
 5            Type: characterInterface,
 6            Args: graphql.FieldConfigArgument{
 7                "id": &graphql.ArgumentConfig{Type: graphql.NewNonNull(graphql.ID)},
 8            },
 9            Resolve: func(p graphql.ResolveParams) (interface{}, error) {
10                id := p.Args["id"].(string)
11                if h, ok := humanData[id]; ok {
12                    return h, nil
13                }
14                if d, ok := droidData[id]; ok {
15                    return d, nil
16                }
17                return nil, nil
18            },
19        },
20        "search": &graphql.Field{
21            Type: graphql.NewList(searchResultUnion),
22            Args: graphql.FieldConfigArgument{
23                "text": &graphql.ArgumentConfig{Type: graphql.NewNonNull(graphql.String)},
24            },
25            Resolve: func(p graphql.ResolveParams) (interface{}, error) {
26                text := p.Args["text"].(string)
27                // Simulasi pencarian
28                results := []interface{}{}
29                for _, h := range humanData {
30                    if strings.Contains(h.Name, text) {
31                        results = append(results, h)
32                    }
33                }
34                for _, d := range droidData {
35                    if strings.Contains(d.Name, text) {
36                        results = append(results, d)
37                    }
38                }
39                for _, s := range starshipData {
40                    if strings.Contains(s.Name, text) {
41                        results = append(results, s)
42                    }
43                }
44                return results, nil
45            },
46        },
47    },
48})

6. Contoh Data Dummy

go
 1var humanData = map[string]Human{
 2    "1000": {"1000", "Luke Skywalker", "Tatooine"},
 3}
 4
 5var droidData = map[string]Droid{
 6    "2000": {"2000", "C-3PO", "Protocol"},
 7}
 8
 9var starshipData = map[string]Starship{
10    "3000": {"3000", "Millennium Falcon", "YT-1300"},
11}

Query Contoh

Query untuk interface:

graphql
 1{
 2  character(id: "1000") {
 3    id
 4    name
 5    ... on Human {
 6      homePlanet
 7    }
 8    ... on Droid {
 9      primaryFunction
10    }
11  }
12}

Query untuk union:

graphql
 1{
 2  search(text: "Sky") {
 3    ... on Human {
 4      id
 5      name
 6      homePlanet
 7    }
 8    ... on Droid {
 9      id
10      name
11      primaryFunction
12    }
13    ... on Starship {
14      id
15      name
16      model
17    }
18  }
19}

7. Alur Eksekusi dengan Interface dan Union

Mari visualisasikan dengan diagram alur menggunakan Mermaid.

MERMAID
flowchart TD
    Q1([Client Query])
    S1([Server])
    RT1{Apakah Interface?}
    RT2{Apakah Union?}
    TYPE1([Resolve Type])
    RESP([Response])

    Q1 --> S1
    S1 --> RT1
    RT1 -- Ya --> TYPE1
    RT1 -- Tidak --> RT2
    RT2 -- Ya --> TYPE1
    RT2 -- Tidak --> RESP
    TYPE1 --> RESP

8. Tabel Perbandingan Interface vs Union

AspekInterfaceUnion
Field yang harus dimilikiWajib memiliki field tertentu (sesuai interface)Tidak wajib punya field yang sama
Kegunaan utamaPolymorphism dengan field yang seragamPolymorphism alternatif tanpa field seragam
Contoh kasusUser: Admin, Guest, MemberHasil pencarian yang bisa berbagai tipe objek

9. Testing Endpoint dengan Go

Anda bisa menguji endpoint dengan code berikut:

go
 1schema, _ := graphql.NewSchema(graphql.SchemaConfig{
 2    Query: rootQuery,
 3})
 4
 5params := graphql.Params{
 6    Schema:        schema,
 7    RequestString: `{ search(text: "Sky") { ... on Human { id name homePlanet } } }`,
 8}
 9
10result := graphql.Do(params)
11fmt.Printf("%v", result.Data)

Outputnya akan berupa JSON sesuai type yang dikembalikan.


Penutup

Menggunakan Interface dan Union Types di graphql-go membuat API kita lebih fleksibel dan siap menangani kebutuhan bisnis yang kompleks. Dengan pendekatan ini, aplikasi backend Go Anda akan lebih maintainable, scalable, serta mudah diintegrasikan dengan client yang heterogen.

Jika Anda membangun startup atau proyek besar dengan Go, pastikan selalu memanfaatkan fitur schema GraphQL ini. Integrasikan dengan testing, monitoring, dan dokumentasi yang baik agar developer experience seluruh tim selalu terjaga.

Selamat bereksperimen dan membangun API GraphQL yang solid dengan graphql-go! 🚀

Artikel Terkait

💬 Komentar