Nuxt3で外部APIを呼び出せなかった時の対応策

みなさんこんにちは!
イザナギです。
さて!前回に引き続き、Nuxt3でブログを作っていた時の話を書いていきたいと思います。
私はヘッドレスCMSとして、microcmsを利用しているのですが、vueファイル内でuseFeachを利用して、ブログデータを取得しようとしていました。
しかし、ブラウザで確認してみるとCORSエラーが炸裂!
何!?表示できないだと...
対応策を調べてみると、サクッと見つかりました。(同じような事象に悩んでいた先人がいらっしゃいました。)
https://qiita.com/tikaranimaru/items/bbbd1f972dc238121d03
なるほど!こういう時に「server」フォルダを使うのですね。


import type { IncomingMessage, ServerResponse } from "http";
import axios from 'axios'
import config from '#config'

let url: string = `https://${config.MICRO_CMS_SERVICE_DOMAIN}.microcms.io/api/v1/blog`
const API_HEAD = {
  headers: {
    "X-MICROCMS-API-KEY": config.MICRO_CMS_API_KEY,
  }
}

export default async (req: IncomingMessage, res: ServerResponse) => {
  if (req.method != 'GET') {
    console.log(req.method)
    res.statusCode = 500
    res.end()
  }


  let data: Array
  await axios.get(
    `${url}`,
    API_HEAD
  ).then(res => {
    data = res.data;
  });
  const json = JSON.stringify(data)

  res.statusCode = 200
  res.setHeader('Content-Type', 'application/json')
  res.end(json)
}

上記のように「server/api/microcms.ts」というファイルを作成すると、「localhost:3000/api/microcms」にmicrocmsのjsonデータが出力されるようになりました!(上記コードは「rc.12」までは使用できましたが、「rc.13」から使えなくなっていました...。使えるように修正したコードは、ページ下「追記(2022/10/19):」を参照ください!)

なるほど。
こっからuseFeachしてくれば良いということね!
早速、vueファイル内に以下を記述し実行してみます。(Swiperと組み合わせてみました。)


<template>
    <v-container id="blog_caloucel_box" class="mx-auto">
        <h2 class="text-center text-h2">Latest Article</h2>
        <swiper
            :modules="modules"
            :slides-per-view="views"
            :autoplay="{ disableOnInteraction: false, }"
        >
            <swiper-slide v-for="content in data['contents']" :key="content.id">
                <a :href="`/blog/articles/${content.id}`">
                    <picture>
                        <source :srcset="`${content.thumbnail.url}?fm=webp&h=${imageWidth}`" />
                        <img :src="`${content.thumbnail.url}?h=${imageWidth}`" />
                    </picture>
                    <p>{{ content.title }}</p>
                </a>
            </swiper-slide>
        </swiper>
    </v-container>
</template>

<script>
// import Swiper core and required modules
import { A11y, Autoplay } from 'swiper';

// Import Swiper Vue.js components
import { Swiper, SwiperSlide } from 'swiper/vue';

// Import Swiper styles
import 'swiper/css';

// Import Swiper styles
export default {
    components: {
        Swiper,
        SwiperSlide,
    },
    setup() {
        let views = ref(0);
        let { data } = useFetch("/api/microcms")

        const imageWidth = ref(300);


        const calculateWindowWidth = () => {
            let windowWidth = window.innerWidth
            views.value = Math.floor(windowWidth / imageWidth.value)
        }

        onMounted(() => {
            calculateWindowWidth();
            window.addEventListener('resize', calculateWindowWidth)
        })

        onBeforeUnmount(() => {
            window.addEventListener('resize', calculateWindowWidth)
        })


        return {
            modules: [A11y, Autoplay],
            data,
            views,
            imageWidth,
        };
    },
};
</script>

<style lang="scss" scoped>
img {
    width: 100%;
    height: 200px;
    position: relative;
}

p {
    position: absolute;
    color: white;
    top: 75%;
    width: 100%;
    margin: 0;
    padding: 0;
    height: 22%;
    background-color: rgba(129, 212, 250, 0.5);
    display: -webkit-box;
    -webkit-box-orient: vertical;
    -webkit-line-clamp: 2;
    overflow: hidden;
    text-overflow: ellipsis;
}

@media screen and (max-width: 600px) {
    p {
        top: 85%;
        -webkit-line-clamp: 1;
    }
}

h2 {
    margin-bottom: 10vw;
    font-family: Great Vibes !important;
}

#blog_caloucel_box {
    // max-width: 80%;
    // margin: 0 auto;
    padding: 0 10px;
    margin-bottom: 100px;
    margin-top: 20vw;
}
</style>


うまく表示できてますね!
そしてちゃっかり、Swiper8も使えることも確認!
さてどんどんブログの作成進めていきますか〜

追記(2022/10/19):

rc.12にアップデートした際に、「defineEventHandler」を代わりに使ってね!みたいなエラーが発生したので、
「server」フォルダ配下に作成したmicrocmsの記事取得用のコードを以下のように修正。
※「defineEventHandler」を使う以外にも多少改造してます。


const config = useRuntimeConfig();
let url: string = `https://${config.MICRO_CMS_SERVICE_DOMAIN}.microcms.io/api/v1/blog`

export default defineEventHandler(async (event) => {

  const q = getQuery(event);

  const post_id: string = !!q.id ? "/" + q.id : "";
  const post_offset = !!q.offset ? q.offset : "";
  const post_limit = !!q.limit ? q.limit : "10";
  const post_order = !!q.order ? q.order : "";
  const post_fields = !!q.fields ? q.fields : "";
  const filter = q.filter ? `${q.filterKeyName}[contains]${q.filter}` : "";
  const query = !!q.query ? q.query : "";

  const parameters: string = `${post_id}?fields=${post_fields}&filters=${filter}&limit=${post_limit}&offset=${post_offset}&orders=${post_order}&q=${query}`;
  const ENDPOINT: string = url + encodeURI(parameters);

  let data: unknown;

  await $fetch(`${ENDPOINT}`, {
    method: "GET",
    headers: {
      "X-MICROCMS-API-KEY": config.MICRO_CMS_API_KEY,
      'Content-Type': 'application/json',
      'Accept-Encoding': 'gzip',
      'Accept-Charset': 'utf-8',
      'Content-Encoding': 'gzip',
    }
  }
  ).then((response) => {
    data = response
  })

  const json = data

  return json;
})

■関連記事(精度そんな良くないかもwww)

■関連トピック

2022 - Izanagi's Site