YoutubeAPIを使ってランダム動画検索アプリを開発してみた。

# 【自己紹介】
皆様はじめまして。**なる**と申します。
私は、文系大学卒業後に銀行へ入社し、現在4年目で法人営業に従事しています。

私がエンジニアを目指す理由はここでは割愛しますが、**学習のアウトプット**等を載せて
いきたいと思います。機会があればエンジニアを目指す理由もお話したいと思います。

また、8月には **42Tokyo** の入試試験  **Piscine** にチャレンジする予定ですので、
そちらのお話もできればと思います。

よろしくお願いします。

## 【今回開発するアプリについて】
今回は、**Youtubeの動画をランダムに検索するアプリ**を制作していきます。

完成したアプリのURLはこちら：
[RandomYoutube](https://random-youtube-gilt.vercel.app/ "Youtubeランダム検索")


作成した目的は **フィルターバブルの解消** です。

「**フィルターバブル**」とは、アルゴリズムがネット利用者個人の検索履歴やクリック履歴を
分析し学習することで、個々のユーザーにとっては望むと望まざるとにかかわらず見たい
情報が優先的に表示され、利用者の観点に合わない情報からは隔離され、自身の考え方や
価値観の「バブル（泡）」の中に孤立するという情報環境を指す。

皆さんも感じることがあるでしょう。
Youtubeを見ていると似たような動画ばかりがオススメに上がってきて、なかなか他の
ジャンルの動画を見ることが無い。面白い動画が見たいがどのように検索をかけるべきか
わからない。

私自身そう感じることが多く、他にも同じように感じる人が多くいるのではと考え、制作を
始めました。
新しい世界と出会うことで、人生が豊かになるのは間違いないですからね。

## 【YoutubeAPI】

#### 実装したい処理
　**検索キーワード、アップロード日、並び順** で動画を検索し、
 　**動画ID、サムネイル、タイトル、概要、再生回数、アップロード日**を取得する。

#### コード記述前の準備
　**Google Cloud**の設定　
 　   （プロジェクト作成・YoutubeAPiの有効化・APIキーの取得）
　
#### コードの実装

　1 **.envファイル**
  　  取得したAPIキーを環境変数に格納します。
 ```javascript:.env
 YOUTUBE_API_KEY = "******************"
 ```
　2  **.gitignoreファイル**
   　 .envファイルをGithubにpushしないよう.gitignoreファイルに格納します。
 ```javascript:gitignore
 # .gitignore
.env
 ```
 　3 **API用ファイル**
  　下記のファイルはルートディレクトリに配置します。
   ```javascript:pages/api/search.tsx
   import { NextApiRequest, NextApiResponse } from "next";

const YOUTUBE_SEARCH_URL = "https://www.googleapis.com/youtube/v3/search";
const YOUTUBE_VIDEO_DETAILS_URL =
  "https://www.googleapis.com/youtube/v3/videos";
const API_KEY = process.env.YOUTUBE_API_KEY;

export default async function searchVideos(
  req: NextApiRequest,
  res: NextApiResponse
) {
  const {
    query,
    publishedAfter,
    maxResults = "5",
    order = "relevance",
    videoDuration = "medium",
  } = req.query;
  //サーチ内容の型を確認
  const queryStr = typeof query === "string" ? query : "";
  const publishedAfterStr =
    typeof publishedAfter === "string" ? publishedAfter : "";
  const maxResultsStr = typeof maxResults === "string" ? maxResults : "5";
  const orderStr = typeof order === "string" ? order : "relevance";
  const videoDurationStr =
    typeof videoDuration === "string" ? videoDuration : "medium";

  // queryStr が空の場合には400 エラーを返す
  if (!queryStr) {
    res.status(400).json({ error: "query parameters error" });
    return;
  }
  //
  try {
    const searchParams = new URLSearchParams({
      part: "snippet",
      q: queryStr,
      type: "video",
      publishedAfter: publishedAfterStr,
      maxResults: maxResultsStr,
      order: orderStr,
      videoDuration: videoDurationStr,
      key: API_KEY as string,
    });

    // パラメータにより検索した動画を JSON 形式で取得
    const response = await fetch(`${YOUTUBE_SEARCH_URL}?${searchParams}`);
    const videoData = await response.json();

    // 取得したデータから各 videoId を取得し文字列作成
    const videoIds = videoData.items
      .map((item: any) => item.id.videoId)
      .join(",");

    // リクエストにより取得したい詳細データを指定
    const detailsParams = new URLSearchParams({
      part: "snippet,statistics",
      id: videoIds,
      key: API_KEY as string,
    });

    // 動画の詳細データを取得
    const detailsResponse = await fetch(
      `${YOUTUBE_VIDEO_DETAILS_URL}?${detailsParams}`
    );
    const detailsData = await detailsResponse.json();

    res.status(200).json(detailsData.items.slice(0, Number(maxResults)));
  } catch (error) {
    console.error("YouTube APIにてエラーが発生:", error);
    res.status(500).json({ error: "Server Error" });
  }
}
```
 　 4 **メイン画面の検索ボタン実装**
     
   ```javascript:src/app/(main)/pages.tsx
    const handleSearch = async () => {
    //タブとキーワードのいずれかが選択されていない場合はモーダルを表示
    if (!checkInputs()) {
      setIsModalOpen(true);
      return;
    }
    //ローディング表示
    setLoading(true);
    try {
      const response = await fetch(
        //クエリとオーダーはエンコードする必要あり
        `/api/search?query=${encodeURIComponent(
          keyword
        )}&publishedAfter=${selectedDate}&maxResults=5&order=${encodeURIComponent(
          selectedOrder
        )}`
      );
      //APIから帰ってきたデータをJSON形式でVideosへ格納
      const data: Video[] = await response.json();
      setVideos(data);
      setSearchClick(true);
    } catch (error) {
      console.error("動画取得時にエラー発生：", error);
    }
    setLoading(false);
```
あとは取得した情報を表示して完成です。
## 【感想】
APIの使用方法が理解出来てきました。Udemy等の動画講義は非常にわかりやすいですが、
実際に自分で0から実装することでより理解が進みますね。また、一見難しそうな公式
ドキュメントを読み込むことが意外と近道だと感じました。
最後まで読んで頂き、ありがとうございました！


## 【参考文献・記事】

https://developers.google.com/youtube/v3?hl=ja

https://qiita.com/kaburankattara/items/240a457f79ade800a4fb