ดึงข้อมูล Graphql API ด้วย React + urql
ตัวอย่างการใช้งาน GraphQL บนโปรเจ็ค React.js เพื่อดึงข้อมูล API ผ่าน Graphql โดยตัวอย่างจะใช้ Public API ที่เป็น Country Code Graphql จากเว็บนี้ GraphQL Countries (เนื่องจากเป็นตัวอย่าง สุ่มเอาครับ เพราะบาง api ก็ link เสียหลายตัวเหมือนกัน)
- Graphql Url - https://countries.trevorblades.com/
สำหรับคนที่มองหา Public API อื่นๆ ลองดู repo นี้ได้ครับ
ตัว Graphql Client จะใช้ตัว urql
TLDR; วิธีการใช้งาน urql แบบเร็วๆ
- ติดตั้ง 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

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 CodeHappy Coding ❤️