ดึงข้อมูล 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 นี้ได้ครับ Public GraphQL APIs
- ตัว Graphql Client จะใช้ตัว urql
TLDR; วิธีการใช้งาน urql แบบเร็วๆ
- ติดตั้ง Dependencies
npm install --save urql graphql
- กำหนด Provider และ Client
import { createClient, Provider } from 'urql'
const client = createClient({ url: 'https://countries.trevorblades.com/',})
<Provider value={client}> <App /></Provider>
- ใช้งาน 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 เราสามารถส่ง varaibles ไป 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 เป็น THconst [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 ไปประยุกต์ใช้ในงานอื่นๆ ได้นะครับ ตัวอย่างนี้ก็เป็นเพียงแค่พื้นฐานเท่านั้น สิ่งสำคัญคือ ไปลองฝึกทำดูนะครับ
Happy Coding ❤️
- Authors
-
Chai Phonbopit
เป็น Web Dev ในบริษัทแห่งหนึ่ง ทำงานมา 10 ปีกว่าๆ ด้วยภาษาและเทคโนโลยี เช่น JavaScript, Node.js, React, Vue และปัจจุบันกำลังสนใจในเรื่องของ Blockchain และ Crypto กำลังหัดเรียนภาษา Rust