ดึงข้อมูล Graphql API ด้วย React + urql

Graphql Mar 19, 2023

ตัวอย่างการใช้งาน GraphQL บนโปรเจ็ค React.js เพื่อดึงข้อมูล API ผ่าน Graphql โดยตัวอย่างจะใช้ Public API ที่เป็น Country Code Graphql จากเว็บนี้ GraphQL Countries (เนื่องจากเป็นตัวอย่าง สุ่มเอาครับ เพราะบาง api ก็ link เสียหลายตัวเหมือนกัน)

สำหรับคนที่มองหา Public API อื่นๆ ลองดู repo นี้ได้ครับ

GitHub - IvanGoncharov/graphql-apis: 📜 A collective list of public GraphQL APIs
📜 A collective list of public GraphQL APIs. Contribute to IvanGoncharov/graphql-apis development by creating an account on GitHub.

ตัว Graphql Client จะใช้ตัว urql

urql Documentation
A highly customisable and versatile GraphQL client.

TLDR; วิธีการใช้งาน urql แบบเร็วๆ

  1. ติดตั้ง Dependencies
npm install --save urql graphql

2. กำหนด Provider และ Client

import { createClient, Provider } from 'urql'

const client = createClient({
  url: 'https://countries.trevorblades.com/',
})

<Provider value={client}>
  <App />
</Provider>

3. ใช้งาน Query ใน App.tsx

import { useQuery } from 'urql'

const QueryCountries = `
  query {
    countries {
      code
      name
    }
  }
`

funciton App() {
  const [result] = useQuery({
    query: QueryCountries
  })
  
  const { data, fetching, error } = result
  
  // render data
}

Mini Workshop

วันนี้เราจะลองมาทำ Workshop ในการดึงข้อมูลประเทศต่างๆ ผ่าน Graphql API กันนะครับ โดยจะใช้ API ฟรีของเว็บนี้ https://countries.trevorblades.com/ ซึ่ง Concept Graphql เราจะใช้แหล่งอื่นๆ ก็ได้เช่น Pokemon API, Starwars, Github API เป็นต้น เพียงแต่ตัวอย่างเลือกใช้ Countries เพราะว่าเน้นตัวอย่างการใช้งาน นั่นเอง

Step 1 - เริ่มต้นสร้างโปรเจ็ค React

เริ่มต้น ทำการสร้างโปรเจ็ค React ด้วย Vite ขึ้นมา โดยตัวอย่างเป็นแบบ TypeScript

npm create vite@latest hello-graphql -- --template react-ts
Vite
Next Generation Frontend Tooling
สามารถดูรายละเอียด Vite เพิ่มเติมได้

Step 2 - ติดตั้ง urql

จากนั้น ผมทำการติดตั้ง urql และ graphql เพื่อจะใช้เป็น Graphql Client

npm install --save urql graphql

Step 3 - สร้าง Client ขึ้นมา

ผมทำการสร้างไฟล์ขึ้นมา ไฟล์นึง ชื่อ graphql-client.ts เอาไว้ที่โฟลเดอร์ src/libs มีข้อมูลแบบนี้ โดยที่มี Query Countries และ Country (แบบ Hardcode TH ไว้อยู่

import { createClient } from 'urql'

export const graphqlClient = createClient({
  url: 'https://countries.trevorblades.com/',
})

export const QueryCountries = `
  query {
    countries {
      code
      name
    }
  }
`

export const QueryCountry = `
  query {
    country(code: "TH") {
      code
      name
      currency
      capital
    }
  }
`

เชื่อมต่อกับ Provider ในหน้า main.tsx

import { Provider } from 'urql'
import { graphqlClient } from './libs/graphql-client'

ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render(
  <React.StrictMode>
    <Provider value={graphqlClient}>
      <App />
    </Provider>
  </React.StrictMode>
)

Step 4 - ลอง Query Countries

ผมทำการลบไฟล์ App.tsx ออกไปหมด แล้วสร้างใหม่แบบนี้ โดยการให้มันไป Query รายชื่อประเทศทั้งหมด โดยใช้ useQuery

import { useQuery } from 'urql'
import './App.css'
import { QueryCountries } from './libs/graphql-client'

interface Country {
  code: string
  name: string
}

function App() {
  const [result] = useQuery({
    query: QueryCountries,
  })

  const { data, fetching, error } = result

  if (fetching) return <p>Loading...</p>
  if (error) return <p>{error.message}</p>

  return (
    <div className="App">
      <ul>
        {data?.countries.map((country: Country) => {
          return (
            <p>
              {country.name} ({country.code})
            </p>
          )
        })}
      </ul>
    </div>
  )
}

export default App

จะเห็นว่า เมื่อเราลองดูหน้าเว็บเรา จะมีรายชื่อประเทศ แสดงออกมา แสดงว่าเรา Query ได้แล้ว

npm run dev

Step 5 - เปลี่ยนมา Query ประเทศบ้าง

จากที่เราใช้ QueryCountries เราก็ลองเปลี่ยนเป็น QueryCountry แทน ก็จะได้แบบนี้

import { useQuery } from 'urql'
import './App.css'
import { QueryCountry } from './libs/graphql-client'

interface Country {
  code: string
  name: string
}

function App() {
  const [result] = useQuery({
    query: QueryCountry,
  })

  const { data, fetching, error } = result

  if (fetching) return <p>Loading...</p>
  if (error) return <p>{error.message}</p>

  return (
    <div className="App">
      <p>{data.country.name}
    </div>
  )
}

export default App

แต่ข้อมูลของ Country ยังเป็น hard code ที่เรากำหนด TH ไว้ในโค๊ดอยู่เลย (ไว้เปลี่ยน dynamic ในตอนถัดไป)

Step 6 - สร้าง Hook สำหรับ Query

ทำเป็น Hooks ให้มันดีกว่า จะได้แยกข้อมูลได้ชัดเจน โดยผมแบ่งเป็น 2 hooks คือ

  • useCountries - สำหรับ Query รายชื่อประเทศ
  • useCountry - สำหรับ Query ประเทศเดียว มีรายละเอียดที่เราอยากได้

สร้างไฟล์ hooks/useCountires.ts ขึ้นมา แบบนี้

import { useQuery } from 'urql'
import { QueryCountries } from '../libs/graphql-client'

const useCountires = () => {
  const [result] = useQuery({
    query: QueryCountries,
  })

  return result
}

export default useCountires

และก็ไฟล์ hooks/useCountry.ts

import { useQuery } from 'urql'
import { QueryCountry } from '../libs/graphql-client'

const useCountry = () => {
  const [result] = useQuery({
    query: QueryCountry,
  })

  return result
}

export default useCountry

ทีนี้ หน้า App.tsx เราอยากจะ query รายชื่อประเทศ หรือ แค่ประเทศเดียว เราก็ใช้ hooks แทน แบบนี้

import { useQuery } from 'urql'
import './App.css'
import { QueryCountries, QueryCountry } from './libs/graphql-client'

interface Country {
  code: string
  name: string
}

function App() {
  const { data, fetching, error } = useCountries()

  if (fetching) return <p>Loading...</p>
  if (error) return <p>{error.message}</p>

  return (
    <div className="App">
      <ul>
        {data?.countries.map((country: Country) => {
          return (
            <p key={country.code}>
              {country.name} ({country.code})
            </p>
          )
        })}
      </ul>
    </div>
  )
}

export default App

Step 7 - urql variables

ตัว urql เราสามารถส่ง variables ไป Query ได้ ซึ่งเราก็ส่งผ่าน useQuery  สิ่งที่เราจะทำคือ เราจะแก้ไข QueryCountry ในไฟล์ graphql-client.ts ให้รับ variable ชื่อ $code ที่นี้ตัว query เราก็จะไม่ใช้ hard code TH แล้ว แต่จะเป็นค่าที่เราส่งมา ผ่าน useQuery

export const QueryCountry = `
  query($code: ID!) {
    country(code: $code) {
      code
      name
      currency
      capital
      emoji
    }
  }
`

ตรงส่วน useCountry.ts ก็เปลี่ยนให้ส่ง variables และรับค่า Props มาด้วย แบบนี้

import { useQuery } from 'urql'
import { QueryCountry } from '../libs/graphql-client'

const useCountry = (code: String) => {
  const [result] = useQuery({
    query: QueryCountry,
    variables: { code },
  })

  return result
}

export default useCountry

แค่นี้ เวลาเราจะเรียก query เราก็ใช้ useCountry('TH') , useCountry('CN') อะไรก็ว่าไป

Step 8 - Dynamic Country

ขั้นตอนนี้เราจะใช้ state เข้ามาช่วยในการเก็บ country code ที่เราเลือก เมื่อเลือกแล้ว มันก็จะไป Query Country มาแสดงนั่นเอง

// 1. default state เป็น TH
const [code, setCode] = useState<string>('TH')

// 2. เวลาเรียก `useCountry()` ก็ส่ง code ไป
const [reuslt] = useCountry(code)

ทีนี้ เราจะ setCode ได้ยังไง? ก็คือ set จาก onClick นั่นเอง ผมแก้ App.tsx ให้มัน render เป็น <button> แทน แบบนี้ เมื่อ click ก็ setCode จากนั้น ก็แสดงผล Country

import { useState } from 'react'
import './App.css'
import useCountires from './hooks/useCountries'
import useCountry from './hooks/useCountry'

interface Country {
  code: string
  name: string
}

function App() {
  const [code, setCode] = useState<string>('TH')

  const { data, fetching, error } = useCountires()
  const { data: countryData, fetching: countryFetching } = useCountry(code)

  if (fetching) return <p>Loading...</p>
  if (error) return <p>{error.message}</p>

  return (
    <div className="App">
      <h1>React.js + Graphql Example</h1>
      {countryFetching ? (
        <p>Loading...</p>
      ) : (
        <div className="card">
          <p>
            {countryData.country.name} {countryData.country.emoji} (
            {countryData.country.code})
          </p>
          <p>Capital: {countryData.country.capital}</p>
          <p>Currency : {countryData.country.currency}</p>
        </div>
      )}

      <ul>
        {data?.countries.map((country: Country) => {
          return (
            <button key={country.code} onClick={() => setCode(country.code)}>
              {country.name}
            </button>
          )
        })}
      </ul>
    </div>
  )
}

export default App

สุดท้าย ก็จะได้หน้าตาเว็บประมาณนี้ สามารถกด แต่ละประเทศ เพื่อแสดงข้อมูลได้

สรุป

หวังว่า Tutorial นี้จะทำให้ผู้อ่าน / ผู้เรียน ได้เห็นภาพ และสามารถทำ React + Graphql ไปประยุกต์ใช้ในงานอื่นๆ ได้นะครับ ตัวอย่างนี้ก็เป็นเพียงแค่พื้นฐานเท่านั้น สิ่งสำคัญคือ ไปลองฝึกทำดูนะครับ

Source Code

Happy Coding ❤️

Tags

Chai Phonbopit

เป็น Web Dev ทำงานมา 10 ปีหน่อยๆ ด้วยภาษา JavaScript, Node.js, React, Vue และปัจจุบันกำลังสนใจ Web3, Crypto และ Blockchain เขียนบล็อกที่ https://devahoy.com