Contoh Penerapan GraphQL Dengan Vue JS - CRUDPRO

Contoh Penerapan GraphQL Dengan Vue JS

Contoh Penerapan GraphQL Dengan Vue JS

GraphQL adalah bahasa kueri untuk API Anda, dan runtime sisi server untuk mengeksekusi kueri. Satu titik akhir dapat mengembalikan data tentang banyak sumber daya, yang membuatnya sangat cocok untuk aplikasi satu halaman Vue.js.

Artikel ini akan membahas cara membangun GraphQL API dari awal, serta mendefinisikan dan mengimplementasikan kueri dan mutasi dengan tipe khusus. Saya akan menggunakan Node.js untuk server GraphQL, dan membuat permintaan serta menampilkan hasilnya menggunakan aplikasi satu halaman Vue.js.

Kode sumber untuk artikel ini ada di sini.

Contoh Penerapan GraphQL Dengan Vue JS

Pengantar

Layanan GraphQL dibuat dengan mendefinisikan jenis dan bidang, kemudian menyediakan fungsi untuk setiap bidang pada setiap jenis. Contoh kanonik dari dokumentasi GraphQL adalah:

type Query { // define the query
  me: User // define the fields
}type User { // define the type
  id: ID
  name: String
}function Query_me(request) { // define the function
  return request.auth.user
}

Di atas adalah bagaimana kami mengimplementasikan kueri, tipe khusus, dan titik akhir menggunakan GraphQL. Kueri sisi klien yang cocok terlihat seperti ini:

{
  me {
   name
  }
}

yang mengembalikan:

{
  "me": {
    "name": "username"
  }
}

Dokumentasi GraphQL resmi sangat bagus, tetapi saya merasa kurang dalam contoh praktis tentang cara mengkueri data menggunakan klien HTTP standar, dan mengintegrasikannya ke dalam aplikasi Vue.js saya, jadi saya akan melakukan hal berikut di artikel ini. Kami akan menggunakan vue-cli baru untuk membuat perancah proyek Vue untuk mengikutinya.

Memulai

Instal vue-cli dengan menjalankan:

npm install -g @vue/cli@latest

Buat proyek baru dengan menjalankan:

vue create graphql-example

dan pergi dengan default dengan memilih default (babel, eslint). Satu ton modul anggukan akan dipasang. Kita juga perlu membuat folder untuk server API, jadi jalankan perintah berikut setelah cd ke dalam proyek (cd graphql-example)

mkdir server
npm install express express-graphql graphql --save

Kami menambahkan graphql, serta express dan express-graphql, yang merupakan lapisan tipis yang menerapkan beberapa praktik terbaik dan pedoman untuk melayani kueri melalui HTTP.

Permintaan Dasar

Mari siapkan kueri sederhana untuk memastikan semuanya berfungsi, dan lihat seperti apa server GraphQL. Di dalam server/index.js, memerlukan beberapa modul:

const express = require('express')
const { graphql, buildSchema } = require('graphql')
const graphqlHTTP = require('express-graphql')
const cors = require('cors')
  • express dan express-graphql akan memungkinkan kami menanggapi permintaan HTTP
  • buildSchema digunakan untuk mendefinisikan tipe (lebih cepat)
  • cors akan memungkinkan kami membuat permintaan dari aplikasi Vue kami, yang akan berjalan pada port 8080, ke server yang berjalan pada port 4000

Hal berikutnya yang harus dilakukan adalah menentukan skema — jenis kueri dan jenis apa yang akan digunakan server. Skema pertama kami pada dasarnya adalah "halo dunia" dari GraphQL:

const schema = buildSchema(`
  type Query {
    language: String
  }
`)

Kami mendefinisikan jenis Query yang disebut bahasa. Ini mengembalikan sebuah String. GraphQL diketik secara statis — bidang memiliki tipe, dan jika ada sesuatu yang tidak cocok, dan kesalahan akan muncul.

Tidak seperti REST API, Graph API hanya memiliki satu titik akhir, yang merespons semua permintaan. Ini disebut pemecah masalah. Saya akan memanggil rootValue milik saya, dan menyertakan implementasi untuk kueri bahasa:

const rootValue = {
  language: () => 'GraphQL'
}

bahasa baru saja mengembalikan sebuah String. Jika kami mengembalikan tipe yang berbeda, misalnya 1 atau {}, kesalahan akan muncul, karena ketika kami mendeklarasikan bahasa dalam skema, kami menetapkan String akan dikembalikan.

Langkah terakhir adalah membuat aplikasi ekspres, dan memasang resolver, rootValue , dan skema.

const app = express()
app.use(cors())app.use('/graphql', graphqlHTTP({
  rootValue, schema, graphiql: true
}))app.listen(4000, () => console.log('Listening on 4000')

Sekarang mari kita implementasikan aplikasi Vue sisi klien, yang akan membuat permintaan.

Membuat permintaan

Buka src/App.vue, dan hapus boilerplate. Sekarang seharusnya terlihat seperti ini:

<template>
  <div id="app">
  </div>
</template><script>
import axios from 'axios'export default {
  name: 'app'
}
</script>

Kami juga mengimpor axios, yang akan kami gunakan untuk membuat permintaan HTTP.

Secara default, graphqlHTTP mendengarkan permintaan POST. Menurut rekomendasi dalam melayani melalui HTTP, kita harus menyertakan query dan variabel di badan permintaan. Ini akan membawa kita ke permintaan berikut:

axios.post('http://localhost:4000/graphql', {
  query: '{ language }'
})

Kueri harus berada di dalam kurung kurawal. Menambahkan tombol untuk memicu permintaan, dan variabel untuk menyimpan hasilnya, kita akan mendapatkan:

<template>
  <div id="app">
    <h3>Example 1</h3>
    <div>
      Data: {{ example1 }}
    </div>
    <button @click="getLanguage">Get Language</button>
    <hr> 
  </div>
</template><script>
import axios from 'axios'export default {
  name: 'app',  data () {
    return {
      example1: ''
    }
  },  methods: {
    async getLanguage () {
      try {
        const res = await axios.post(
          'http://localhost:4000/graphql', {
          query: '{ language }'
        })
        this.example1 = res.data.data.language
      } catch (e) {
        console.log('err', e)
      }
    }
  }
}
</script>

Mari kita jalankan ini. Di satu terminal, mulai server GraphQL dengan node server . Di tempat lain, jalankan aplikasi Vue menggunakan npm run serve. Kunjungi http://localhost:8080. Jika semuanya berjalan dengan baik, Anda akan melihat:

Contoh Penerapan GraphQL Dengan Vue JS

Klik "Dapatkan Bahasa" akan mengembalikan dan merender hasilnya.

OK bagus. Sejauh ini kami:

  • mendefinisikan skema
  • membuat resolver, rootValue
  • buat permintaan menggunakan aksioma, yang menyertakan query

Apa lagi yang bisa kita lakukan dengan GraphQL?

Jenis Kustom dengan Model

GraphQL memungkinkan kita mendefinisikan jenis kustom, dan objek untuk mewakili mereka dalam bahasa target — dalam hal ini, JavaScript, tetapi ada klien GraphQL untuk sebagian besar bahasa sisi server. Saya akan mendefinisikan tipe Champion dalam skema, dan mencocokkan kelas ES6 untuk menyimpan properti dan metode apa pun.

Pertama, perbarui skema:

const schema = buildSchema(`
  type Query {
    language: String
  }  type Champion {
     name: String
     attackDamage: Float
   }
`)

Tidak ada yang terlalu menarik selain tipe baru, Float. Selanjutnya kita dapat mendefinisikan kelas ES6 untuk mewakili tipe ini, dan menyimpan metode instan atau data tambahan apa pun. Saya akan mendefinisikan ini di file baru, server/champion.js.

class Champion {
  constructor(name, attackDamage) {
    this.name = name
    this.attackDamage = attackDamage
  }
}module.exports = Champion

Tidak ada yang istimewa, hanya kelas ES6. Perhatikan bahwa kami memiliki nama dan attackDamage — bidang yang sama yang ditentukan dalam skema untuk Champion.

Sekarang, mari buat kueri lain yang menggunakan tipe Champion. Skema yang diperbarui adalah sebagai berikut:

const schema = buildSchema(`
  type Query {
    language: String
    getChampions: [Champion]
  }  type Champion {
    name: String
    attackDamage: Float
  }
`)

getChampions mengembalikan serangkaian Champion. Besar! Untuk menyelesaikan contoh ini, beberapa data tiruan dan titik akhir lainnya:

const champions = [
  new Champion('Ashe', 100),
  new Champion('Vayne', 200)
]const rootValue = {
  language: () => 'GraphQL',  getChampions: () => champions
}

Restart server dengan menekan ctrl+c di terminal yang menjalankan server, dan jalankan node server lagi. Mari kita verifikasi apakah ini berfungsi, dengan mengirimkan kueri dari klien.

Meminta bidang tertentu

Menanyakan getChampions sedikit lebih menarik daripada bahasa. Kali ini hasilnya akan berisi tipe Champion yang ditentukan pengguna — dan bidang apa pun yang kami minta. GraphQL mengharuskan kita untuk eksplisit di bidang mana yang kita inginkan. Misalnya query berikut:

{ 
  getChampions 
}

tidak akan berfungsi. Setidaknya satu bidang harus ditentukan. Kueri yang diperbarui:

{
  getChampions {
    name
  }
}

Pengembalian:

{
  "data": {
    "getChampions": [
      {
        "name": "Ashe"
      },
      {
        "name": "Vayne"
      }
    ]
  }
}

Perhatikan hanya nama yang dikembalikan! Jika kami menyertakan attackDamage, kami juga akan mendapatkannya. Kueri:

{
  getChampions {
    name
    attackDamage
  }
}

dan tanggapan:

{
  "data": {
    "getChampions": [
      {
        "name": "Ashe"
        "attackDamage": 100
      },
      {
        "name": "Vayne"
        "attackDamage": 200
      }
    ]
  }
}

Menerapkan ini di aplikasi Vue sama mudahnya:

<template>
  <div id="app">
    <!-- ... -->    <h3>Example 2</h3>
    <div>
      Data:
      <div v-for="champion in champions">
        {{ champion }}
      </div>
    </div>
    <button @click="getChampions">Get Champions</button>
  </div>
</template>export default {
  name: 'app',  data () {
    return {
      /* ... */,
      champions: []
    }
  },  methods: {
    /* ... */
    async getChampions () {
      const res = await axios.post(
        'http://localhost:4000/graphql', {
        query: `{
          getChampions {
            name
          }
        }`
      })
      this.champions = res.data.data
    }
  }
}

Pastikan Anda me-restart server dengan server node, jika Anda belum melakukannya. Tidak perlu me-restart aplikasi Vue, karena hot reload webpack akan otomatis diperbarui saat Anda menyimpan perubahan apa pun.

Mengklik "Dapatkan Juara" menghasilkan:

Contoh Penerapan GraphQL Dengan Vue JS

Melewati Argumen

getChampions mengembalikan semua juara. GraphQL juga mendukung argumen yang lewat, untuk mengembalikan subset data. Ini membutuhkan:

  • objek variabel tambahan di badan POST
  • memberi tahu permintaan sisi klien jenis argumen yang akan Anda parsing ke kueri dari variabel.

Mari kita terapkan kueri getChampionByName. Seperti biasa, mulailah dengan definisi kueri:

const schema = buildSchema(`
  type Query {
    language: String
    getChampions: [Champion]
    getChampionByName(name: String!): Champion
  }  type Champion {
    name: String
    attackDamage: Float
  }
`)

Perhatikan kita mendeklarasikan nama argumen, dan tipe String!. ! berarti argumen diperlukan.

Selanjutnya implementasinya:

const rootValue = {
  language: () => 'GraphQL',  getChampions: () => champions,  getChampionByName: ({ name }) => {
    return champions.find(x => x.name === name)
  }
}

Tidak ada yang terlalu menarik — kami hanya menggunakan find untuk mendapatkan juara yang sesuai. Perbaikan akan menambahkan beberapa penanganan kesalahan, dan membandingkan nama kasus mengabaikan.

Sekarang, implementasi sisi klien. Di sinilah segalanya menjadi sedikit lebih menarik. Saat meneruskan argumen, kita harus memberi nama kueri, dan mendeklarasikan argumen dengan tipe yang sesuai:

async getChampionByName () {
  const res = await axios.post('http://localhost:4000/graphql', {
    query: `
      query GetChampionByName($championName: String!) {
        getChampionByName(name: $championName) {
          name
          attackDamage
        }
      }`,
      variables: {
        championName: 'Ashe'
      }
  })
  this.champion = res.data.data.getChampionByName
}

Baris demi baris:

  1. kueri GetChampionByName adalah nama yang kami berikan untuk kueri. Ini bisa apa saja, tetapi harus deskriptif tentang apa yang dilakukan kueri. Dalam hal ini, karena kami hanya memanggil getChampionByName, saya menggunakan konvensi ketika namanya sama dengan kueri di sisi server, tetapi menggunakan huruf kapital untuk huruf pertama. Dalam aplikasi nyata, satu panggilan API mungkin menyertakan banyak operasi berbeda. Memberi nama kueri dapat membuat kode lebih mudah dipahami.
  2. ($championName: String!) berarti variabel harus berisi nama champion, dan ini bukan opsional.
  3. getChampionByName(name: $championName) adalah kueri yang akan dieksekusi di sisi server. Argumen pertama, nama, harus menggunakan nilai championName di objek variabel.
  4. Kami meminta nama dan attackDamage sebagai tanggapan.

Beberapa markup tambahan akan memungkinkan kami menampilkan hasilnya di aplikasi Vue (jangan lupa untuk me-restart server GraphQL):

<template>
  <div>  <!-- ... -->  <h3>Example 4</h3>
  Name: <input v-model="name">
  <div>
    Data:
    {{ champion }}
  </div>
  <button @click="getChampionByName">Get Champion</button>  </div>
</template><script>
import axios from 'axios'export default {
  data () {
    return { 
      /* ... */ 
      champion: {}
    }
  },methods: {    /* ... */    async getChampionByName () {
      const res = await axios.post(
        'http://localhost:4000/graphql', {
        query: `
          query GetChampionByName($championName: String!) {
            getChampionByName(name: $championName) {
              name
              attackDamage
            }
          }`,
          variables: {
            championName: 'Ashe'
          }
      })
      this.champion = res.data.data.getChampionByName
    }
  }
}
Contoh Penerapan GraphQL Dengan Vue JS

Memperbarui Catatan

Sejauh ini, kami baru saja mengambil data. Anda juga sering ingin memperbarui data, itulah sebabnya GraphQL juga menyediakan mutasi. Sintaks dan implementasinya tidak terlalu jauh dari apa yang telah kita bahas sejauh ini. Mari kita mulai dengan mendefinisikan mutasi:

const schema = buildSchema(`
  type Query {
    language: String
    getChampions: [Champion]
    getChampionByName(name: String!): Champion
  }  type Mutation {
    updateAttackDamage(name: String!, attackDamage: Float): Champion
  }  type Champion {
    name: String
    attackDamage: Float
  }
`)

Mutasi masuk dalam tipe Mutasi. Sintaks lainnya harus sudah familier pada titik ini. Kami mengembalikan catatan yang diperbarui, tipe Champion. Implementasinya sama mudahnya:

const rootValue = {
  language: () => 'GraphQL',  getChampions: () => champions,  getChampionByName: ({ name }) => {
    return champions.find(x => x.name === name)
  },  updateAttackDamage: ({ name, attackDamage = 150 }) => {
    const champion = champions.find(x => x.name === name)
    champion.attackDamage = attackDamage    return champion
  }
}

Dalam contoh yang lebih realistis, Anda mungkin menjalankan kueri SQL untuk memperbarui catatan dalam database, atau melakukan beberapa validasi. Kami harus mengembalikan tipe Champion, karena kami menentukannya dalam deklarasi mutasi. GraphQL akan secara otomatis memilih bidang yang benar untuk dikembalikan, berdasarkan permintaan — kami akan menanyakan nama dan memperbarui attackDamage, seperti yang ditunjukkan di bawah ini:

methods: {
  /* ... */  async updateAttackDamage () {
    const res = await axios.post('http://localhost:4000/graphql', {
      query: `
        mutation UpdateAttackDamage(
          $championName: String!,  $attackDamage: Float) {
          updateAttackDamage(name: $championName, attackDamage: $attackDamage) {
            name
            attackDamage
          }
        }`,
        variables: {
          championName: this.name,
          attackDamage: this.attack
        }
    })
    this.updatedChampion = res.data.data.updateAttackDamage
  }
}

Satu-satunya perbedaan nyata di sini adalah kami mendeklarasikan nama operasi sebagai tipe mutasi alih-alih tipe kueri.

Contoh yang diperbarui sepenuhnya adalah sebagai berikut:

<template>
  <div>
  <!-- ... -->
  <h3>Example 4</h3>
    Name: <input v-model="name">
    Attack Damage: <input v-model.number="attack">
    <div>
      Data:
      {{ updatedChampion }}
    </div>
    <button @click="updateAttackDamage">Update Champion</button>  </div>
</template><script>
import axios from 'axios'export default {  data () {
    return {
      /* ... */
      updatedChampion: {},
      attack: 5.5
    }
  },  methods: {
    /* ... */
    async updateAttackDamage () {
      const res = await axios.post('http://localhost:4000/graphql', {
        query: `
        mutation UpdateAttackDamage($championName: String!, $attackDamage: Float) {
          updateAttackDamage(name: $championName, attackDamage: $attackDamage) {
            name
            attackDamage
          }
        }`,
        variables: {
          championName: this.name,
          attackDamage: this.attack
        }
      })
      this.updatedChampion = res.data.data.updateAttackDamage
    }
  }
}

Seperti biasa, restart server GraphQL. Hasilnya adalah sebagai berikut:

Contoh Penerapan GraphQL Dengan Vue JS

Anda dapat mengklik "Dapatkan Juara", dan lihat apakah data disimpan dengan benar (seharusnya mengembalikan kerusakan serangan yang baru diperbarui):

Contoh Penerapan GraphQL Dengan Vue JS

Pengujian

Saya tidak melewati pengujian. Namun, menguji titik akhir sisi server adalah esay, karena ini hanyalah JavaScript biasa — cukup ekspor objek rootValue, dan uji fungsi seperti biasanya. Saya akan mengeksplorasi pengujian GraplQL API di posting mendatang.

Kesimpulan

Ada banyak hal lain yang dapat dilakukan GraphQL. Baca lebih lanjut di situs resmi. Saya berharap untuk mengeksplorasi lebih banyak di posting mendatang. Ini adalah alternatif yang menyegarkan untuk REST, dan sangat cocok untuk aplikasi satu halaman berbasis Vue.js.