Devahoy Logo
PublishedAt

React

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

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

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

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

  1. ติดตั้ง Dependencies
Terminal window
npm install --save urql graphql
  1. กำหนด Provider และ Client
import { createClient, Provider } from 'urql'
const client = createClient({
url: 'https://countries.trevorblades.com/',
})
<Provider value={client}>
<App />
</Provider>
  1. ใช้งาน 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

Terminal window
npm create vite@latest hello-graphql -- --template react-ts

Step 2 - ติดตั้ง urql

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

Terminal window
npm install --save urql graphql

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

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

graphql-client.ts
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

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

App.tsx
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 ได้แล้ว

Terminal window
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 ขึ้นมา แบบนี้

hooks/useCountries.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

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 แทน แบบนี้

App.tsx
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 เป็น 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 ไปประยุกต์ใช้ในงานอื่นๆ ได้นะครับ ตัวอย่างนี้ก็เป็นเพียงแค่พื้นฐานเท่านั้น สิ่งสำคัญคือ ไปลองฝึกทำดูนะครับ

Happy Coding ❤️

Authors
avatar

Chai Phonbopit

เป็น Web Dev ในบริษัทแห่งหนึ่ง ทำงานมา 10 ปีกว่าๆ ด้วยภาษาและเทคโนโลยี เช่น JavaScript, Node.js, React, Vue และปัจจุบันกำลังสนใจในเรื่องของ Blockchain และ Crypto กำลังหัดเรียนภาษา Rust

Related Posts