Teknologi GraphQL selama beberapa tahun terakhir, setelah perusahaan Facebook memindahkannya ke kategori open-source, telah menjadi sangat populer. Penulis materi, terjemahan yang kami terbitkan hari ini, mengatakan bahwa ia mencoba bekerja dengan GraphQL di Node.js dan, dari pengalamannya sendiri, yakin bahwa teknologi ini, berkat kemampuan dan kesederhanaannya yang luar biasa, tidak sengaja menarik begitu banyak perhatian. Baru-baru ini, saat terlibat dalam proyek baru, ia beralih dari Node.js ke Golang. Kemudian dia memutuskan untuk menguji kolaborasi Golang dan GraphQL.

Informasi awal
Anda bisa belajar dari definisi GraphQL resmi bahwa ini adalah bahasa permintaan untuk API dan runtime untuk mengeksekusi pertanyaan seperti itu pada data yang ada. GraphQL memberikan deskripsi data yang lengkap dan dapat dipahami dalam API tertentu, memungkinkan pelanggan untuk meminta informasi yang dibutuhkan secara tepat, dan tidak lebih, menyederhanakan pengembangan API dari waktu ke waktu dan memberi pengembang alat yang kuat.
Tidak banyak perpustakaan GraphQL untuk Golang. Secara khusus, saya mencoba perpustakaan seperti
Thunder ,
graphql ,
graphql-go , dan
gqlgen . Saya harus mencatat bahwa yang terbaik dari semua yang saya coba adalah perpustakaan gqlgen.
Pustaka gqlgen masih dalam versi beta, pada saat penulisan materi ini, versi
0.7.2 . Perpustakaan berkembang pesat.
Di sini Anda dapat mengetahui tentang rencana pengembangannya. Sekarang sponsor resmi gqlgen adalah proyek desain 99, yang berarti bahwa perpustakaan ini, sangat mungkin, akan berkembang lebih cepat daripada sebelumnya. Pengembang utama perpustakaan ini adalah
vektah dan
neelance , sementara neelance, selain itu, berfungsi di perpustakaan graphql-go.
Mari kita bicara tentang perpustakaan gqlgen berdasarkan asumsi bahwa Anda sudah memiliki pengetahuan dasar tentang GraphQL.
Fitur Gqlgen
Dalam deskripsi gqlgen, Anda dapat mengetahui apa yang kami miliki sebelum kami adalah pustaka untuk membuat server GraphQL yang diketik dengan ketat di Golang. Frasa ini tampaknya sangat menjanjikan bagi saya, karena itu berarti bahwa ketika bekerja dengan perpustakaan ini saya tidak akan menemukan sesuatu seperti
map[string]interface{}
, karena pendekatan berdasarkan pengetikan ketat digunakan di sini.
Selain itu, perpustakaan ini menggunakan pendekatan yang didasarkan pada skema data. Ini berarti bahwa API dijelaskan menggunakan
Bahasa Definisi Skema GraphQL. Bahasa ini memiliki alat pembuat kode sendiri yang kuat yang secara otomatis membuat kode GraphQL. Dalam hal ini, programmer hanya dapat mengimplementasikan logika dasar dari metode antarmuka yang sesuai.
Artikel ini dibagi menjadi dua bagian. Yang pertama dikhususkan untuk metode kerja dasar, dan yang kedua untuk yang maju.
Metode kerja utama: pengaturan, permintaan untuk menerima dan mengubah data, langganan
Kami, sebagai aplikasi eksperimental, akan menggunakan situs tempat pengguna dapat mempublikasikan video, menambahkan tangkapan layar dan ulasan, mencari video dan melihat daftar rekaman yang terkait dengan catatan lain. Mari kita mulai mengerjakan proyek ini:
mkdir -p $GOPATH/src/github.com/ridhamtarpara/go-graphql-demo/
Buat file skema data berikut (
schema.graphql
) di direktori root proyek:
type User { id: ID! name: String! email: String! } type Video { id: ID! name: String! description: String! user: User! url: String! createdAt: Timestamp! screenshots: [Screenshot] related(limit: Int = 25, offset: Int = 0): [Video!]! } type Screenshot { id: ID! videoId: ID! url: String! } input NewVideo { name: String! description: String! userId: ID! url: String! } type Mutation { createVideo(input: NewVideo!): Video! } type Query { Videos(limit: Int = 25, offset: Int = 0): [Video!]! } scalar Timestamp
Berikut ini dijelaskan model data dasar, satu mutasi (
Mutation
, deskripsi permintaan untuk perubahan data), yang digunakan untuk mempublikasikan file video baru di situs, dan satu permintaan (
Query
) untuk mendapatkan daftar semua file video. Baca lebih lanjut tentang skema GraphQL di
sini . Selain itu, di sini kami mendeklarasikan salah satu tipe data skalar kami sendiri. Kami tidak puas dengan 5
tipe data skalar standar (
Int
,
Float
,
String
,
Boolean
dan
ID
) yang ada di GraphQL.
Jika Anda perlu menggunakan tipe Anda sendiri, Anda dapat mendeklarasikannya di
schema.graphql
(dalam kasus kami, tipe ini adalah
Timestamp
) dan memberikan definisi mereka dalam kode. Saat menggunakan pustaka gqlgen, Anda perlu menyediakan metode untuk membuat dan meng-unmarshaling semua jenis skalar Anda sendiri dan mengonfigurasi pemetaan menggunakan
gqlgen.yml
.
Perlu dicatat bahwa dalam versi terbaru perpustakaan ada satu perubahan penting. Yaitu, ketergantungan pada file biner yang dikompilasi dihapus dari itu. Oleh karena itu, file
scripts/gqlgen.go
harus ditambahkan ke proyek
scripts/gqlgen.go
konten berikut:
Setelah itu, Anda perlu menginisialisasi
dep
:
dep init
Sekarang saatnya untuk mengambil keuntungan dari kemampuan pembuatan kode perpustakaan. Mereka memungkinkan Anda untuk membuat semua kode boilerplate yang membosankan, yang, bagaimanapun, tidak dapat disebut sepenuhnya tidak menarik. Untuk memulai mekanisme pembuatan kode otomatis, jalankan perintah berikut:
go run scripts/gqlgen.go init
Sebagai hasil dari eksekusi, file-file berikut akan dibuat:
gqlgen.yml
: file konfigurasi untuk mengelola pembuatan kode.
generated.go
: kode yang dihasilkan.
models_gen.go
: semua model dan tipe data dari skema yang disediakan.
resolver.go
: ini akan menjadi kode yang dibuat oleh programmer.
server/server.go
: titik masuk dengan http.Handler
untuk memulai server GraphQL.
Lihatlah model yang dibuat untuk jenis
Video
(file
generated_video.go
):
type Video struct { ID string `json:"id"` Name string `json:"name"` User User `json:"user"` URL string `json:"url"` CreatedAt string `json:"createdAt"` Screenshots []*Screenshot `json:"screenshots"` Related []Video `json:"related"` }
Di sini Anda dapat melihat bahwa
ID
adalah sebuah string,
CreatedAt
juga sebuah string. Model terkait lainnya dikonfigurasi sesuai. Namun, dalam aplikasi nyata ini tidak diperlukan. Jika Anda menggunakan semua jenis data SQL, maka Anda perlu, misalnya, bahwa bidang
ID
akan, tergantung pada database yang digunakan, tipe
int
atau
int64
.
Sebagai contoh, saya menggunakan PostgreSQL dalam aplikasi demo ini, jadi tentu saja saya memerlukan bidang
ID
untuk menjadi tipe
int
dan
CreatedAt
jenis
time.Time
. Waktu. Ini mengarah pada fakta bahwa kita perlu mendefinisikan model kita sendiri dan memberi tahu gqlgen bahwa kita perlu menggunakan model kita alih-alih menghasilkan yang baru. Berikut ini isi file
models.go
:
type Video struct { ID int `json:"id"` Name string `json:"name"` Description string `json:"description"` User User `json:"user"` URL string `json:"url"` CreatedAt time.Time `json:"createdAt"` Related []Video }
Kami memberi tahu perpustakaan bahwa itu harus menggunakan model ini (file
gqlgen.yml
):
schema: - schema.graphql exec: filename: generated.go model: filename: models_gen.go resolver: filename: resolver.go type: Resolver models: Video: model: github.com/ridhamtarpara/go-graphql-demo/api.Video ID: model: github.com/ridhamtarpara/go-graphql-demo/api.ID Timestamp: model: github.com/ridhamtarpara/go-graphql-demo/api.Timestamp
Maksud dari semua ini adalah bahwa kita sekarang memiliki definisi kita sendiri untuk
ID
dan
Timestamp
dengan metode untuk marshaling dan unmarshaling dan memetakannya dalam file
gqlgen.yml
. Sekarang pengguna menyediakan string sebagai
ID
, metode
UnmarshalID()
mengubah string itu menjadi integer. Saat mengirim respons, metode
MarshalID()
mengubah angka menjadi string. Hal yang sama terjadi dengan
Timestamp
atau dengan jenis skalar lain yang dideklarasikan oleh programmer.
Sekarang saatnya mengimplementasikan logika aplikasi. Buka file
resolver.go
dan tambahkan deskripsi mutasi dan kueri ke dalamnya. Sudah ada kode boilerplate yang dihasilkan secara otomatis yang harus kita isi dengan maknanya. Ini kode untuk file ini:
func (r *mutationResolver) CreateVideo(ctx context.Context, input NewVideo) (api.Video, error) { newVideo := api.Video{ URL: input.URL, Name: input.Name, CreatedAt: time.Now().UTC(), } rows, err := dal.LogAndQuery(r.db, "INSERT INTO videos (name, url, user_id, created_at) VALUES($1, $2, $3, $4) RETURNING id", input.Name, input.URL, input.UserID, newVideo.CreatedAt) defer rows.Close() if err != nil || !rows.Next() { return api.Video{}, err } if err := rows.Scan(&newVideo.ID); err != nil { errors.DebugPrintf(err) if errors.IsForeignKeyError(err) { return api.Video{}, errors.UserNotExist } return api.Video{}, errors.InternalServerError } return newVideo, nil } func (r *queryResolver) Videos(ctx context.Context, limit *int, offset *int) ([]api.Video, error) { var video api.Video var videos []api.Video rows, err := dal.LogAndQuery(r.db, "SELECT id, name, url, created_at, user_id FROM videos ORDER BY created_at desc limit $1 offset $2", limit, offset) defer rows.Close(); if err != nil { errors.DebugPrintf(err) return nil, errors.InternalServerError } for rows.Next() { if err := rows.Scan(&video.ID, &video.Name, &video.URL, &video.CreatedAt, &video.UserID); err != nil { errors.DebugPrintf(err) return nil, errors.InternalServerError } videos = append(videos, video) } return videos, nil }
Sekarang mari kita uji mutasi.
Mutasi createVideoItu berhasil! Tetapi mengapa tidak ada informasi
user
(objek
user
)? Saat bekerja dengan GraphQL, konsep yang mirip dengan pemuatan "malas" (lazy) dan "serakah" (bersemangat) dapat diterapkan. Karena sistem ini dapat dikembangkan, Anda perlu menentukan bidang mana yang harus diisi "dengan rakus" dan mana yang "malas".
Saya menyarankan kepada tim di organisasi tempat saya bekerja dengan "aturan emas" berikut yang berlaku ketika bekerja dengan gqlgen: "Jangan memasukkan dalam model bidang yang perlu dimuat hanya jika diminta oleh klien."
Dalam kasus kami, saya perlu mengunduh data tentang klip video terkait (dan bahkan informasi pengguna) hanya jika klien meminta bidang ini. Tetapi karena kami memasukkan bidang ini dalam model, gqlgen mengasumsikan bahwa kami menyediakan data ini dengan menerima informasi tentang video. Hasilnya, sekarang kita mendapatkan struktur kosong.
Kadang-kadang terjadi bahwa jenis data tertentu diperlukan setiap kali, sehingga tidak praktis untuk mengunduhnya menggunakan permintaan terpisah. Untuk ini, untuk meningkatkan kinerja, Anda dapat menggunakan sesuatu seperti SQL join. Sekali (ini, bagaimanapun, tidak berlaku untuk contoh yang dipertimbangkan di sini), saya perlu mengunggah metadata bersama dengan video. Entitas-entitas ini disimpan di tempat yang berbeda. Akibatnya, jika sistem saya menerima permintaan untuk mengunduh video, saya harus membuat permintaan lain untuk mendapatkan metadata. Tetapi, karena saya tahu tentang persyaratan ini (yaitu, saya tahu bahwa klien dan video dan metadata-nya selalu diperlukan di sisi klien), saya lebih suka menggunakan teknik pemuatan serakah untuk meningkatkan kinerja.
Mari kita menulis ulang model dan menghasilkan kode gqlgen lagi. Agar tidak menyulitkan cerita, kami hanya menulis metode untuk bidang
user
(file
models.go
):
type Video struct { ID int `json:"id"` Name string `json:"name"` Description string `json:"description"` UserID int `json:"-"` URL string `json:"url"` CreatedAt time.Time `json:"createdAt"` }
Kami menambahkan
UserID
dan menghapus struktur
User
. Sekarang buat ulang kode:
go run scripts/gqlgen.go -v
Berkat perintah ini, metode antarmuka berikut akan dibuat untuk menyelesaikan struktur yang tidak ditentukan. Selain itu, Anda perlu menentukan yang berikut ini di resolver (file
generated.go
):
type VideoResolver interface { User(ctx context.Context, obj *api.Video) (api.User, error) Screenshots(ctx context.Context, obj *api.Video) ([]*api.Screenshot, error) Related(ctx context.Context, obj *api.Video, limit *int, offset *int) ([]api.Video, error) }
Berikut ini definisi (file
resolver.go
):
func (r *videoResolver) User(ctx context.Context, obj *api.Video) (api.User, error) { rows, _ := dal.LogAndQuery(r.db,"SELECT id, name, email FROM users where id = $1", obj.UserID) defer rows.Close() if !rows.Next() { return api.User{}, nil } var user api.User if err := rows.Scan(&user.ID, &user.Name, &user.Email); err != nil { errors.DebugPrintf(err) return api.User{}, errors.InternalServerError } return user, nil }
Sekarang, hasil tes mutasi akan terlihat seperti yang ditunjukkan di bawah ini.
Mutasi createVideoApa yang baru saja kita bahas adalah dasar-dasar GraphQL, setelah menguasainya, Anda sudah bisa menulis sesuatu sendiri. Namun, sebelum Anda terjun ke eksperimen dengan GraphQL dan Golang, akan berguna untuk berbicara tentang langganan, yang secara langsung terkait dengan apa yang kami lakukan di sini.
โ Berlangganan
GraphQL menyediakan kemampuan untuk berlangganan perubahan data yang terjadi secara real time. Perpustakaan gqlgen memungkinkan, secara real time, menggunakan soket web, untuk bekerja dengan acara berlangganan.
Langganan harus dijelaskan dalam file
schema.graphql
. Berikut ini deskripsi berlangganan ke acara penerbitan video:
type Subscription { videoPublished: Video! }
Sekarang, jalankan pembuatan kode otomatis lagi:
go run scripts/gqlgen.go -v
Seperti yang telah disebutkan, selama pembuatan kode secara otomatis dalam file
generated.go
, sebuah antarmuka dibuat yang harus diimplementasikan dalam pengenal. Dalam kasus kami, sepertinya ini (file
resolver.go
):
var videoPublishedChannel map[string]chan api.Video func init() { videoPublishedChannel = map[string]chan api.Video{} } type subscriptionResolver struct{ *Resolver } func (r *subscriptionResolver) VideoPublished(ctx context.Context) (<-chan api.Video, error) { id := randx.String(8) videoEvent := make(chan api.Video, 1) go func() { <-ctx.Done() }() videoPublishedChannel[id] = videoEvent return videoEvent, nil } func (r *mutationResolver) CreateVideo(ctx context.Context, input NewVideo) (api.Video, error) {
Sekarang, saat membuat video baru, Anda perlu memicu suatu peristiwa. Dalam contoh kami, ini dilakukan pada baris
for _, observer := range videoPublishedChannel
.
Sekarang saatnya untuk memeriksa langganan Anda.
Verifikasi BerlanggananGraphQL, tentu saja, memiliki kemampuan berharga tertentu, tetapi seperti yang mereka katakan, tidak semua yang berkilau itu emas. Yaitu, kita berbicara tentang fakta bahwa seseorang yang menggunakan GraphQL perlu mengurus otorisasi, kompleksitas permintaan, caching, masalah permintaan N +1, keterbatasan kecepatan eksekusi permintaan, dan beberapa hal lainnya. Jika tidak, sistem yang dikembangkan menggunakan GraphQL dapat menghadapi penurunan kinerja yang serius.
Teknik Lanjut: Otentikasi, Pemuat Data, Kompleksitas Kueri
Setiap kali saya membaca manual seperti ini, saya merasa bahwa, setelah menguasainya, saya mempelajari semua yang perlu saya ketahui tentang teknologi tertentu dan mendapatkan kemampuan untuk menyelesaikan masalah kompleksitas apa pun.
Tetapi ketika saya mulai mengerjakan proyek saya sendiri, saya biasanya masuk ke situasi yang tidak terduga yang terlihat seperti kesalahan server atau seperti permintaan yang telah berjalan lama, atau seperti beberapa situasi jalan buntu lainnya. Sebagai akibatnya, untuk melakukan ini, saya harus mempelajari apa yang baru-baru ini tampak sangat bisa dimengerti. Dalam manual yang sama ini, saya berharap ini bisa dihindari. Itulah sebabnya di bagian ini kita akan melihat beberapa teknik canggih untuk bekerja dengan GraphQL.
โ Otentikasi
Saat bekerja dengan REST API, kami memiliki sistem otentikasi dan alat otorisasi standar saat bekerja dengan titik akhir tertentu. Tetapi ketika menggunakan GraphQL, hanya satu titik akhir yang digunakan, oleh karena itu, tugas otentikasi dapat diselesaikan menggunakan arahan skema. Edit file
schema.graphql
sebagai berikut:
type Mutation { createVideo(input: NewVideo!): Video! @isAuthenticated } directive @isAuthenticated on FIELD_DEFINITION
Kami membuat arahan
isAuthenticated
dan menerapkannya pada langganan
createVideo
. Setelah sesi pembuatan kode otomatis berikutnya, Anda perlu menentukan definisi untuk arahan ini. Sekarang arahan diterapkan dalam bentuk metode struktur, dan bukan dalam bentuk antarmuka, jadi kita perlu menggambarkannya. Saya mengedit kode yang dibuat secara otomatis yang terletak di file
server.go
dan membuat metode yang mengembalikan konfigurasi GraphQL untuk file
server.go
. Ini adalah file
resolver.go
:
func NewRootResolvers(db *sql.DB) Config { c := Config{ Resolvers: &Resolver{ db: db, }, }
Ini adalah file
server.go
:
rootHandler:= dataloaders.DataloaderMiddleware( db, handler.GraphQL( go_graphql_demo.NewExecutableSchema(go_graphql_demo.NewRootResolvers(db) ) ) http.Handle("/query", auth.AuthMiddleware(rootHandler))
Kami membaca
ID
pengguna dari konteksnya. Apakah Anda tidak menemukan ini aneh? Bagaimana makna ini masuk ke dalam konteks dan mengapa itu muncul dalam konteks? Faktanya adalah bahwa gqlgen menyediakan konteks permintaan hanya pada tingkat implementasi, jadi kami tidak memiliki cara untuk membaca data permintaan HTTP apa pun, seperti header atau cookie, dalam pengenal atau arahan. Akibatnya, Anda perlu menambahkan mekanisme perantara Anda sendiri ke sistem, menerima data ini dan memasukkannya ke dalam konteks.
Sekarang kita perlu menggambarkan mekanisme otentikasi perantara kita sendiri untuk mendapatkan data otentikasi dari permintaan dan memverifikasinya.
Tidak ada logika yang didefinisikan di sini. Sebagai gantinya, untuk data otorisasi, untuk tujuan demonstrasi,
ID
pengguna hanya diteruskan di sini. Mekanisme ini kemudian digabungkan dalam
server.go
dengan metode pemuatan konfigurasi baru.
Sekarang deskripsi arahan masuk akal. Kami tidak memproses permintaan pengguna yang tidak sah dalam kode middleware, karena permintaan tersebut akan diproses oleh arahan. Ini tampilannya.
Bekerja dengan pengguna yang tidak sahBekerja dengan pengguna yang diotorisasiSaat bekerja dengan arahan skema, Anda bahkan dapat menyampaikan argumen:
directive @hasRole(role: Role!) on FIELD_DEFINITION enum Role { ADMIN USER }
โData loader
Menurut saya semua ini terlihat cukup menarik. Anda mengunduh data saat Anda membutuhkannya. Klien memiliki kemampuan untuk mengelola data, persis apa yang dibutuhkan diambil dari penyimpanan. Tapi semuanya punya harga.
Berapa harga yang harus dibayar untuk peluang ini? Lihatlah log unduhan semua video. Yaitu, kita berbicara tentang fakta bahwa kita memiliki 8 video dan 5 pengguna.
query{ Videos(limit: 10){ name user{ name } } }
Detail Pengunduhan Video Query: Videos : SELECT id, name, description, url, created_at, user_id FROM videos ORDER BY created_at desc limit $1 offset $2 Resolver: User : SELECT id, name, email FROM users where id = $1 Resolver: User : SELECT id, name, email FROM users where id = $1 Resolver: User : SELECT id, name, email FROM users where id = $1 Resolver: User : SELECT id, name, email FROM users where id = $1 Resolver: User : SELECT id, name, email FROM users where id = $1 Resolver: User : SELECT id, name, email FROM users where id = $1 Resolver: User : SELECT id, name, email FROM users where id = $1 Resolver: User : SELECT id, name, email FROM users where id = $1
Apa yang sedang terjadi di sini? Mengapa ada 9 permintaan (1 permintaan dikaitkan dengan tabel video dan 8 - dengan tabel pengguna)? Terlihat mengerikan. Hati saya hampir berhenti ketika saya berpikir bahwa API kami yang ada harus diganti dengan ini ... Benar, pemuat data dapat sepenuhnya mengatasi masalah ini.
Ini dikenal sebagai masalah N + 1. Kita berbicara tentang fakta bahwa ada satu query untuk mendapatkan semua data dan untuk setiap bagian data (N) akan ada query lain ke database.
Ini adalah masalah yang sangat serius dalam hal kinerja dan sumber daya: meskipun permintaan ini paralel, mereka menguras sumber daya sistem.
Untuk mengatasi masalah ini, kita akan menggunakan pustaka
dataloaden dari penulis pustaka gqlgen. Perpustakaan ini memungkinkan Anda untuk menghasilkan kode Go. Pertama, buat pemuat data untuk entitas
User
:
go get github.com/vektah/dataloaden dataloaden github.com/ridhamtarpara/go-graphql-demo/api.User
Kami siap menggunakan file
userloader_gen.go
, yang memiliki metode seperti
Fetch
,
LoadAll
dan
Prime
.
Sekarang, untuk mendapatkan hasil umum, kita perlu mendefinisikan metode
Fetch
(file
dataloader.go
):
func DataloaderMiddleware(db *sql.DB, next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { userloader := UserLoader{ wait : 1 * time.Millisecond, maxBatch: 100, fetch: func(ids []int) ([]*api.User, []error) { var sqlQuery string if len(ids) == 1 { sqlQuery = "SELECT id, name, email from users WHERE id = ?" } else { sqlQuery = "SELECT id, name, email from users WHERE id IN (?)" } sqlQuery, arguments, err := sqlx.In(sqlQuery, ids) if err != nil { log.Println(err) } sqlQuery = sqlx.Rebind(sqlx.DOLLAR, sqlQuery) rows, err := dal.LogAndQuery(db, sqlQuery, arguments...) defer rows.Close(); if err != nil { log.Println(err) } userById := map[int]*api.User{} for rows.Next() { user:= api.User{} if err := rows.Scan(&user.ID, &user.Name, &user.Email); err != nil { errors.DebugPrintf(err) return nil, []error{errors.InternalServerError} } userById[user.ID] = &user } users := make([]*api.User, len(ids)) for i, id := range ids { users[i] = userById[id] i++ } return users, nil }, } ctx := context.WithValue(r.Context(), CtxKey, &userloader) r = r.WithContext(ctx) next.ServeHTTP(w, r) }) }
Di sini kita tunggu 1 ms. sebelum menjalankan permintaan dan mengumpulkan permintaan dalam paket hingga 100 permintaan. Sekarang, alih-alih mengeksekusi permintaan untuk setiap pengguna secara individual, loader akan menunggu waktu yang ditentukan sebelum mengakses database. Selanjutnya, Anda perlu mengubah logika pengenal dengan mengkonfigurasi ulang menggunakan permintaan untuk menggunakan pemuat data (file
resolver.go
):
func (r *videoResolver) User(ctx context.Context, obj *api.Video) (api.User, error) { user, err := ctx.Value(dataloaders.CtxKey).(*dataloaders.UserLoader).Load(obj.UserID) return *user, err }
Begini cara log terlihat setelah itu dalam situasi yang mirip dengan yang dijelaskan di atas:
Query: Videos : SELECT id, name, description, url, created_at, user_id FROM videos ORDER BY created_at desc limit $1 offset $2 Dataloader: User : SELECT id, name, email from users WHERE id IN ($1, $2, $3, $4, $5)
Hanya dua permintaan basis data yang dieksekusi di sini, sebagai hasilnya, semua orang sekarang senang. Sangat menarik untuk dicatat bahwa hanya 5 pengidentifikasi pengguna yang dikirim ke permintaan, meskipun data diminta untuk 8 video. Ini menunjukkan bahwa pemuat data menghapus catatan duplikat.
โ
GraphQL API , . , API DOS-.
, .
Video
, . GraphQL
Video
. . โ .
, โ :
{ Videos(limit: 10, offset: 0){ name url related(limit: 10, offset: 0){ name url related(limit: 10, offset: 0){ name url related(limit: 100, offset: 0){ name url } } } } }
100, . (, , ) , .
gqlgen , . , (
handler.ComplexityLimit(300)
) GraphQL (300 ). , (
server.go
):
rootHandler:= dataloaders.DataloaderMiddleware( db, handler.GraphQL( go_graphql_demo.NewExecutableSchema(go_graphql_demo.NewRootResolvers(db)), handler.ComplexityLimit(300) ), )
, , . 12. , , , ( , , , , ).
resolver.go
:
func NewRootResolvers(db *sql.DB) Config { c := Config{ Resolvers: &Resolver{ db: db, }, }
, , .
, ,
related
. , , , , .
Ringkasan
, ,
GitHub . . , , .
Pembaca yang budiman! GraphQL , Go?
