JSONPlaceholder APIを使った検索アプリ

Published
2026-01-31
Author
MT
Tags

はじめに


どうも、エンジニア歴5ヶ月目の者です。

早いもので、もうエンジニアとして5ヶ月目に入りました。


業務では、改修から本番デプロイまでの全体の流れを、ある程度自走して進められるようになってきました。

ただ、やはり自分の担当チケットがすんなり本番にデプロイされることは、まだ少ないのが現状です。


たとえば、チケット内のエラーを修正したと思ったら、別の箇所で新しいエラーが発生したり。

テスト環境で検証を進める中で、思わぬ箇所に影響が出たりすることもあります。


そうした想定外の連鎖を経験する中で、影響範囲を考慮しながら改修を進める難しさを日々感じています。

そして、テストの重要性も改めて実感するようになりました。


本題

ということで、ここからは、本題です。


これまでに、TodoアプリAPIを使ったランダム取得+お気に入り保存アプリなどを作ってきました。

今回はそのステップアップとして、JSONPlaceholderのAPIを利用した検索アプリを作ってみました。


👉 アプリはこちら🔗 https://json-placeholder-plum.vercel.app/


JSONPlaceholderの/usersエンドポイントを利用し、

ユーザー名を検索して該当ユーザーを一覧表示できるアプリを実装しました。


UIはカード形式にして、よくある商品一覧アプリのように、

APIを叩いて取得したデータを一覧表示する構成にしました。


今回も開発にはVue.js(Vue CLI)を使用しています。


コードの説明


まず、大前提として、こちらの記事を参考にさせていただきました。

記事内では、検索欄に文字を入力するとリアルタイムで検索結果が表示される仕組みが紹介されていました。

この部分を学びのベースとしつつ、今回は自分なりに機能を拡張してみました。


具体的には、「入力中に検索結果が変わる」形式から変更し、

Enterキーを押したタイミングで検索結果が表示される仕様にしています。

小さな変更ではありますが、ユーザー操作のタイミングを明確にすることで、

より意図的な検索体験になるよう工夫しました。


なお、記事ではAxiosを使用してAPIを取得していましたが、

今回はFetch APIを使用してデータを取得しています。


メソッドは主に下記の二つになります。

    async searchApi() {
      try {
        const url = 'https://jsonplaceholder.typicode.com/users'
        const res = await fetch(url)
        const data = await res.json()
        this.lists = data
        this.results = this.lists
      } catch (error) {
        console.log(error);
      }
    },
    submitSearch(search) {
      const searchWord = search.trim()
      if (searchWord === '') {
        this.results = this.lists
        return
      }
      this.results = this.lists.filter(list => {
        return (
          list.name.toLowerCase().includes(searchWord.toLowerCase())
        )
      })
    },


検索ロジック


まず、ユーザーが input に文字を入力し、

検索ボタンを押すとメソッドが発火します。

<input type="text"
   v-model="search"
   placeholder="名前で検索…"
   @keyup.enter="submitSearch(search)"
/>
<button @click="submitSearch(search)">Search</button>

submitSearch() メソッドが呼び出され、

引数として v-model="search" にバインドされている入力値が渡されます。



入力値の整形(trim()

const searchWord = search.trim()

ここで入力文字列の前後の空白を取り除いています。

つまり " hoge " → "hoge" のようにして、

余分な空白による検索ミスを防ぎます。


これは先ほど紹介した記事を参考にして実装しました。

先頭や末尾に空白があると検索がうまく動かなくなることがあるため、

trim() を使うことでそうした不具合を回避できることを学びました。



空文字チェック

if (searchWord === '') {
  this.results = this.lists
  return
}

もし何も入力されていなければ、

初期状態の全データ(APIで取得した this.lists)を再表示します。



検索処理(filterを使った結果表示)

this.results = this.lists.filter(list => {
  return (
    list.name.toLowerCase().includes(searchWord.toLowerCase())
  )
})

ここが検索の中核部分です。


this.lists の中身を filter 関数で1件ずつチェックし、

入力値 searchWord の文字列が list.name に含まれているかを判定しています。


一致するユーザー(カード)だけが this.results に格納され、

その結果として、入力した文字列を含むユーザーのカードのみが画面に表示される仕様になっています。


検索対象には、名前(list.name)のほかにメールやウェブサイト(list.email, list.website)なども追加しようと考えました。

しかし、データ件数が少ないため、

たとえば「a」など1文字だけで検索した場合に、ほとんどのカードがヒットしてしまう問題がありました。


そのため今回は、検索対象をユーザー名(list.name)のみに限定して実装しています。



大文字・小文字を区別しない検索


文字列の比較時には、list.name も searchWord も すべて小文字に変換 しています。


たとえば、ユーザーが "L" のように大文字で入力した場合、

そのままだと小文字の "l" を含むデータがヒットしません。


そこで、両方を toLowerCase() で小文字に統一することで、

大文字・小文字を区別せずに検索できるよう に実装しました。


参考:

まとめ


今回は、JSONPlaceholder を使用して簡単な検索アプリを実装しました。

API通信や検索ロジックの基本構造を理解する上で、とても良い学びになりました。


今後は今回の応用として、クエリパラメータを利用した検索機能

カテゴリ選択・複数条件検索など、もう少し発展的なアプリにも挑戦していきたいです。


それではまた。

最後まで読んでいただき、ありがとうございました!