Nuxt.js Fundamental ตอนที่ 6 - การ Fetch ข้อมูลจาก API

Published on

เขียนวันที่ : Aug 22, 2020

LastModified on

(อัพเดท : Mar 19, 2022)

Discord

ต่อมาคือการ Fetch ข้อมูลจาก API นะครับ ใน Vue.js ปกติ เราอาจจะ fetch data โดยผ่าน function mounted() หรือสร้าง function ใน methods อาจจะใช้ fetch หรือ axios ก็ได้

ซึ่งใน Nuxt ผมจะยกตัวอย่างการใช้ axios นะครับ โดยเราสามารถ install ได้แบบปกติเลย

npm install axios

แต่ตัว Nuxt ก็มี Nuxt Axios ที่มีข้อดีคือ

  • Set ตัว baseUrl ให้เรา
  • ง่ายต่อการ setToken หรือทำ authentication
  • inject เป็น global ทำให้เรียกใช้งานได้ง่าย ไม่ต้อง import ทุกครั้ง
  • กำหนด retry เพื่อ request อีกรอบกรณี error / failed
  • ใช้ fetch แบบ isomorphic HTTP requests (fetch ทั้ง Server side และ client side)

วิธีใช้งาน ก็คล้ายๆ Nuxt Content ครับ คือ install ก่อน

npm install @nuxtjs/axios

จากนั้นไปเพิ่ม module ใน nuxt.config.js

nuxt.config.js
module.exports = {
  modules: ['@nuxtjs/axios'],

  axios: {}
};

และกำหนด options ผ่าน object axios ถ้าเรามี environment variable API_URL มันจะ override ค่า baseURL นะครับ

ซึ่งรูปแบบการใช้ fetch หรือ axios ก็เหมือนกับการ request api แบบปกติที่เขียนด้วย Vue.js เลยครับ

การใช้ fetch

fetch(url)
  .then((response) => response.json())
  .then((data) => console.log(data));

การใช้ axios

axios.get(url).then((response) => console.log(response.data));

ใน axios ปกติ เวลาเราได้ข้อมูล response เราต้องดึงข้อมูลจาก response.data ใช่มั้ยครับ แต่ถ้าเราใช้ Nuxt Axios มันจะมี shortcut เราสามารถใช้ได้เลยเช่น $get()

// แบบ axios ปกติ
axios.get(url).then((res) => res.data);

// แบบ Nuxt Axios shortcut ($)
axios.$get(url).then((res) => console.log(res));

Fetching Data ด้วย asyncData

มาลองดึงข้อมูล API กันดีกว่า โดยผมจะใช้เว็บ https://picsum.photos/ เป็น API นะครับ เป็น API ที่ไว้สำหรับดึงข้อมูลรูปภาพ

  • โดยใช้ /v2/list - เพื่อดึง photos แบบ list
  • และ /id/{id}/info - สำหรับรายละเอียดของรูปครับ

ผมสร้างไฟล์ pages/photos/index.vue ขึ้นมา

photos/index.vue
<template>
  <div class="container">
    <h1>Photos</h1>
    <div class="photos">
      <nuxt-link v-for="photo in photos" :to="{ path: `/photos/${photo.id}` }" :key="photo.id">
        <img :src="photo.download_url" class="photo-item" />
        {{ photo.author }}
      </nuxt-link>
    </div>
  </div>
</template>
<script>
  export default {
    head() {
      return {
        title: 'Nuxt Fetch API'
      };
    },
    async asyncData({ $axios }) {
      const photos = await $axios.$get('https://picsum.photos/v2/list?limit=12');
      return { photos };
    }
  };
</script>
<style scoped>
  .container {
    padding: 1em;
    width: 1220px;
    margin: 0 auto;
  }
  .photos {
    display: grid;
    grid-template-columns: 1fr 1fr 1fr 1fr;
    grid-template-rows: auto;
    grid-gap: 1em;
    text-align: center;
  }

  .photo-item {
    width: 100%;
    height: 256px;
    object-fit: cover;
  }
</style>

จากโค๊ดด้านบน หน้า /photos จะดึงข้อมูลรูปแบบ list มาแสดงผล และใช้ <nuxt-link> กำหนด ตาม photo.id เพื่อ link ไป dynamic route ของเรานั่นเอง

<nuxt-link
  v-for="photo in photos"
  :to="{ path: `/photos/${photo.id}` }"
  :key="photo.id"
>

โดย <nuxt-link> เราใช้ :to โดยส่ง object {path: '/url'} ไป เช่น /photos/1 , /photos/2

ต่อมา เราจะกำหนด dynamic routing สำหรับแต่ละรูป โดยสร้างไฟล์ pages/photos/_id.vue ขึ้นมา และทำการรับ route.params จากนั้น ก็ fetch api โดยใช้ asyncData แบบนี้ครับ

photos/_id.vue
<template>
  <div class="photo-info">
    <h1>Photo Detail</h1>
    <img :src="photo.download_url" class="img" />
    <p>Author : {{ photo.author }}</p>

    <nuxt-link to="/photos" class="link">Back to Photos</nuxt-link>
  </div>
</template>
<script>
  export default {
    async asyncData({ $axios, params }) {
      const photo = await $axios.$get(`https://picsum.photos/id/${params.id}/info`);
      return { photo };
    }
  };
</script>

<style scoped>
  .photo-info {
    width: 860px;
    margin: 0 auto;
    text-align: center;
  }
  .img {
    width: 100%;
  }

  .link {
    display: inline-block;
    margin-top: 2em;
  }
</style>

เพียงเท่านี้ เราก็สามารถดึงข้อมูลจาก API ด้วยการใช้ asyncData ได้แล้วครับ

Nuxt Fetch API

ข้อดีของ asyncData คือ?

  • มันจะถูกเรียกทุกครั้ง ก่อน page load มันจะถูกเรียก 1 ครั้งฝั่ง Server Side และทุกๆ ครั้งที่ฝั่ง Client side ตอนที่เรา navigate ด้วย <nuxt-link>
  • เหมาะสำหรับดึงข้อมูลก่อนการ render (pre-render)
  • สามารถ return ได้ทั้ง Promise หรือใช้ async/await (ถ้า Promise จะ resolve ก่อน render component)
  • result ของ asyncData จะรวมกับ data() อัตโนมัติ ทำให้เราไม่ต้องกำหนด function data() เราเข้าถึงค่าใน template ได้เลย
  • เราสามารถกำหนดเงื่อนไขได้ ถ้า asyncData ถุกเรียกตอน Server side ให้ทำอะไร ถ้าเรียกตอนฝั่ง client side ให้ทำอะไร เป็นต้น
  • เข้าถึง dynamic route ได้ ตามตัวอย่าง ที่แสดงรูป photo info เพราะ asyncData เข้าถึง context ได้ และใน context ก็มี params นั่นเอง

อ่าน asyncData เพิ่มเติมได้ที่ NuxtJS - Async Data

Hints & Questions?

เพื่อนๆลองประยุกต์ใช้ asyncData กันดูนะครับ

  • เช่น ให้มัน fetch ทุกๆ ครั้งที่ query เปลี่ยน ด้วย watchQuery
  • fetch() กับ asyncData ต่างกันยังไงนะ?
  • เราอยากให้หน้าแรก /photos fetch ครั้งเดียวพอ ตอน build time ได้มั้ย? ไม่อยากให้ fetch api ทุกครั้ง ตอน navigate ระหว่าง <nuxt-link>
Buy Me A Coffee
Discord