はじめに
自分は10月よりAPPRENTICE SHIP(内定直結型エンジニア学習プログラム)のカリキュラムに2期生として参加しています。
その一環としてチーム開発を行っており、技術情報をまとめることで学習に役立てるよう、記事を作成しております。
概要
今回はチーム開発において外部のAPIサービスを利用する必要が出てきたので、情報をまとめております。
具体的には検索候補(オートコンプリート機能)をフロントエンドで実装する手順をまとめました。
今回使用させて頂くAPIサービス
ANNICT
試聴中のアニメを記録してくれるwebアプリケーションサービスになります。
(※サーバーの関係か画像が読み込まれなかった)
Annict Developers
有志の方により開発が行われており、今回はこちらで公開しているAPIサービス
「Annict developers」を使用させて頂きます。
問題と使用用途
今回開発しているアニメ関係のアプリでタイトルを登録する際に、ユーザー任せで記入してしまうと表記揺れが発生してしまうことが予想されました。
その予防策として、検索式にしてタイトルを選択してもらう機能(オートコンプリート機能)が必要となりました。
またタイトルに応じて話数の取得が必要となり、Annict様よりAPIを使ってデータをお借りする運びとなりました。
そもそもAPIって?
API
ソフトウェア同士が情報をやり取りする際に使用されるインタフェースのことです。より細かく表現すると、アプリケーション同士が接続する際のプロトコルや接続部分の仕様を取り決めたもの、と定義されます。
アプリケーションの開発者は、APIを使ってアプリケーション間の連携がスムーズに行えるよう、意識してプログラムを作成します。
WEB API
APIの中でも、インターネットを経由して情報のやり取りが行われるAPIを「Web API」と呼びます。
Web APIは、インターネット上で情報のやり取りに広く使われているHTTP/HTTPSを利用して、アプリケーション同士が接続する仕組みを提供します。使用するプログラミング言語の仕様に左右されず、汎用的に利用できることが大きな特徴です。
使用準備
Annictのアカウント作成、ログイン
APIを利用するにあたり、こちらのページからアカウントを作成してください。
個人用アクセストークンの生成
アカウント作成が完了したら「設定」⇨「アプリケーション」のタブからアクセストークンの「新規作成」を行なって下さい。
今回はデータの使用なので「読み込み専用」の設定でトークンを作成します。
トークンは一度しか表示されないのでコピーをしておいて下さい。
詳細はこちらに記載があります。
Annict 個人用アクセストークンの作成方法
実装と解説
今回はJavaScriptのライブラリ「AutoComplete.js」を使用してオートコンプリート機能を実装していきたいと思います。
参考サイト: AutoComplete.js活用術
デモ画面
実際のデモ画面がこちらになります。
インプットの検索するキーワードに応じて検索候補が表示されます。
HTML
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>オートコンプリートデモ</title>
<link
href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.0-beta1/dist/css/bootstrap.min.css"
rel="stylesheet"
/>
</head>
<body>
<div class="container mt-5">
<h2>オートコンプリートデモ</h2>
<input
id="autoComplete"
class="form-control"
type="text"
/>
</div>
<!-- autoComplete.jsの読み込み -->
<script src="https://cdn.jsdelivr.net/npm/@tarekraafat/autocomplete.js@10/dist/autoComplete.min.js"></script>
<!-- script.jsの読み込み -->
<script src="script.js"></script>
</body>
</html>
*CSSにはbootstrap5を使用しています。
JavaScript
// API config
const api = "https://api.annict.com/v1/works";
const token = "ご自身のTOKENを入れて下さい";
const annictFetchData = async (query) => {
const params = {
filter_title: query,
};
const queryString = new URLSearchParams(params).toString();
const urlWithParams = `${api}?${queryString}&access_token=${token}`;
const response = await fetch(urlWithParams);
const data = await response.json();
console.log(data);
return data.works; // 作品のリストを返す
};
// autoComplete.js
const autoCompleteJS = new autoComplete({
name: "autoComplete",
selector: "#autoComplete",
wrapper: true,
data: {
src: async (query) => {
const source = await annictFetchData(query);
return source.map((work) => work.title);
},
cache: false,
},
placeHolder: "検索クエリを入力してください...",
debounce: 100, // Milliseconds value
searchEngine: "loose",
resultsList: {
render: true,
container: (source) => {
source.setAttribute("id", "autoComplete_list");
source.classList.add("list-group");
},
},
resultItem: {
highlight: true,
content: (data, source) => {
source.innerHTML = data.match;
source.classList.add("list-group-item");
source.classList.add("list-group-item-action");
},
maxResults: 10,
},
submit: false,
events: {
input: {
selection: (event) => {
const selection = event.detail.selection.value;
autoCompleteJS.input.value = selection;
},
},
},
});
JavaScriptのコードの解説
AnnictAPIを叩きに行く
まずは前段部分のAnnictのAPIを叩く部分になります。
今回は検索キーワードが含まれているタイトルのみ取得したいので、用意されているAPIのパラメータの中から「filter_title」というパラメータに検索キーワードを入れてAPIを叩きにいきます。
// API config
const api = "https://api.annict.com/v1/works";
const token = "ご自身のTOKENを入れて下さい";
ここの部分ではAnnictのAPIと発行したトークンを記述します。
const annictFetchData = async (query) => {
const params = {
filter_title: query,
};
const queryString = new URLSearchParams(params).toString();
const urlWithParams = `${api}?${queryString}&access_token=${token}`;
const response = await fetch(urlWithParams);
const data = await response.json();
return data.works; // 作品のリストを返す
};
次に用意したAPIのURLとトークンを使用して、APIを叩きにいきます。
const params = {
filter_title: query,
};
const queryString = new URLSearchParams(params).toString();
const urlWithParams = `${api}?${queryString}&access_token=${token}`;
ここのquery引数には、インプットに入れたキーワードが入ってきます。
そのあとはparamsをクエリ文字列に変換して、URLを構築しています。
const response = await fetch(urlWithParams);
const data = await response.json();
return data.works; // 作品のリストを返す
残りは構築したURLを元にfetch関数でAPIを叩き、JSON形式に変換後、レスポンスデータ内のworks(検索候補として返ってきた作品リスト)を返しています。
オートコンプリート機能
最後に後段のオートコンプリートの部分となります。
こちらは設定が主になりますので、用意したAPIを叩きに行くannictFetchData関数の使用箇所のみ解説します。
// autoComplete.js
const autoCompleteJS = new autoComplete({
name: "autoComplete",
selector: "#autoComplete",
wrapper: true,
data: {
src: async (query) => {
const source = await annictFetchData(query);
return source.map((work) => work.title);
},
cache: false,
},
placeHolder: "検索クエリを入力してください...",
debounce: 100, // Milliseconds value
searchEngine: "loose",
resultsList: {
render: true,
container: (source) => {
source.setAttribute("id", "autoComplete_list");
source.classList.add("list-group");
},
},
resultItem: {
highlight: true,
content: (data, source) => {
source.innerHTML = data.match;
source.classList.add("list-group-item");
source.classList.add("list-group-item-action");
},
maxResults: 10,
},
submit: false,
events: {
input: {
selection: (event) => {
const selection = event.detail.selection.value;
autoCompleteJS.input.value = selection;
},
},
},
});
data: {
src: async (query) => {
const source = await annictFetchData(query);
return source.map((work) => work.title);
},
cache: false,
}
-
データソース関数の設定:
data: { src: async (query) => { ... } } は、オートコンプリート機能に対して、ユーザーが入力フィールドに何か入力した際に呼び出される非同期関数を設定しています。この関数は、ユーザーの入力(query)を引数として受け取ります。 -
APIリクエストの実行:
const source = await annictFetchData(query); は、ユーザーの入力を引数としてannictFetchData関数(AnnictのAPIを叩く関数)を呼び出し、APIからの応答を待機します。annictFetchDataは、指定された検索クエリに基づいてAnnict APIを叩き、結果を取得します。 -
データの加工と返却:
return source.map((work) => work.title); は、APIから取得したデータ(作品のリスト)を加工しています。ここでは各作品オブジェクトのtitleプロパティを取り出し、それらのタイトルのみの配列を生成しています。この配列がオートコンプリートの候補として表示されます。 -
キャッシュの設定:
cache: false は、オートコンプリート機能のキャッシュを無効にする設定です。これにより、同じ検索クエリに対しても毎回APIへのリクエストが行われることを意味します。
まとめ
今回は外部APIを使用してオートコンプリート機能の作成を行いました。
AutoComplete.js使用しての作成でしたが、初めて使うライブラリとAPIを組み合わせて、こういった機能が実装できるのは驚きでしたし、勉強にもなりました。