一覧へ

遺伝子情報検索ツール:APIとJSONを使って検索ボックスを作成

公開されている遺伝子データベースのAPIをfetch関数で呼び出し、JSON形式のレスポンスを解析して、検索ウィジェットを作成する方法をステップごとに学びます。

中級
|
90
|
検証済み (2026-07)
遺伝子検索RESTful APIJSON取得する、取り出す、連れてくる非同期遺伝子注釈
進捗0/19 (0%)

遺伝子情報検索ツール — APIとJSONを活用した検索ボックスの作成

このトピックを終えると

教科書で学んだ APIJSON を組み合わせて、遺伝子名を入力すると、公開データベースから機能、位置、別名を取得してカード形式で表示する 検索ウィジェットを自分で作成できます。「他人が作成した巨大なデータベースを自分のアプリで使用する」ということが、実際にどのようなコードになるのかを理解します。

この記事は 教育用の一般的な例 です。公開されている無料の遺伝子注釈 API を題材に、ウェブから外部データを取得する一般的なパターンを学びます。


「この遺伝子は何だったかな」——毎回サイトを探し回る問題

実験をしていると、遺伝子名は知っているけれど、詳細が思い出せないことがよくあります。『TP53』はどの染色体にあったかな?『BRCA1』の正式な別名は何だったかな?

そのたびに、ブラウザを開いて、公開データベースサイトにアクセスし、検索窓にキーワードを入力して、結果ページを閲覧します。一度や二度ならまだ良いですが、50個の遺伝子を確認しなければならないとしたら? 50回タブを開けたり閉じたりする地獄が始まります。

そこで開発者の発想が生まれます。「あのサイトも結局、どこかのデータベースからデータを取得して表示しているだけだ。そのデータベースに直接問い合わせることができないだろうか?」それが API です。

まずは完成品を見てみましょう(ブラックボックスを先に実行する)

私たちが作成するウィジェットは、次のように動作します。検索ボックスに遺伝子名を入力すると――

text
┌─────────────────────────────────────────┐
│  🔍  [ TP53            ]  [検索]        │
├─────────────────────────────────────────┤
│  TP53   (tumor protein p53)             │
│  ─────────────────────────────          │
│  📍 位置   : 17p13.1                    │
│  🏷️ 別名   : p53, LFS1, BCC7            │
│  🧬 要約   : 細胞周期の制御、腫瘍抑制   │
└─────────────────────────────────────────┘

ウェブサイトを開かなくても、検索ボックス一つで情報が表示されます。これを実現するために必要なのは、たった2つの概念――どこに問い合わせるか(API)、回答をどのように読み取るか(JSON)です。


このツールはどのような部品で構成されているか(部品分解図)

text
遺伝子情報検索ウィジェット
   ┌─────────────────────────────────────────────────────┐
   │  [質問] リクエストURLの組み立て ──── 部品: API      │  ← 自分で作成 ★
   │              │                                      │
   │              ▼                                      │
   │  [送信] fetchで呼び出し ───── 部品: fetch           │  ← 完成品として提供(ツール)
   │              │            + 非同期                  │
   │              ▼                                      │
   │  [解釈] 応答のパース ──────── 部品: JSON            │  ← 自分で作成 ★
   │              │                                      │
   │              ▼                                      │
   │  [出力] カードのレンダリング                        │
   └─────────────────────────────────────────────────────┘
部品どこで学んだかこのツールで何をするか
APIapi-basics「何を、どこに尋ねるか」URLの作成
fetch/非同期ajax-fetch, sync-vs-async実際にリクエストを送信し、待機
JSONjson-data-format戻ってきた応答から必要な値を取り出す

📌 これらの概念を初めて学ぶ場合(上部のリンク)

自分で作成する新しい概念は、APIとJSONの2つだけです。 実際の通信を行うfetchはツールとして完成品を提供します。

🔎 APIを一言で表すと(ドロー — api-basics) APIはレストランのメニューです。厨房(DB)に直接入ることはできませんが、メニュー(API)に定められた方法で注文すると、料理(データ)が出てきます。私たちは、「この遺伝子情報をください」をメニューのルールに合わせて注文する方法を学びます。


制作步骤 1 — 组装 URL 以询问所需信息 ★ (API)

✍️ 手动填写的部分。 组件 = API。目标:将“提供此基因信息”转换为 API 可以理解的 URL。

向 API 发送请求通常使用 URL。“地址 + 所需内容”按照规则组合即可。例如,假设某个公开的基因 API 具有以下规则。

text
https://example-api.org/query?q={基因名称}&fields={所需项目}
  • q= 后面是搜索词(基因名称)
  • fields= 后面是想要获取的项目(用逗号分隔)

如果手动创建,可能会出现拼写错误或编码错误。因此,我们将创建一个安全组装 URL 的函数。这里重要的是 encodeURIComponent——它可以包裹搜索词,即使搜索词中包含空格或特殊字符,URL 也不会损坏。

javascript
const API_BASE = "https://example-gene-api.org/query";

function buildQueryUrl(geneName, fields = ["symbol", "name", "genomic_pos", "alias", "summary"]) {
  const q = encodeURIComponent(geneName.trim());
  const f = encodeURIComponent(fields.join(","));
  return `${API_BASE}?q=${q}&fields=${f}`;
}

// 验证
console.assert(
  buildQueryUrl("TP53") === "https://example-gene-api.org/query?q=TP53&fields=symbol%2Cname%2Cgenomic_pos%2Calias%2Csummary",
  "基本 URL 组装失败"
);
// 即使是带有空格的搜索词也应安全地进行编码
console.assert(
  buildQueryUrl("  TP 53  ").includes("q=TP%2053"),
  "空格编码失败"
);

第二个 assert 是关键。即使用户输入 TP 53 这样的空格,也会安全地编码为 TP%2053。如果不使用 encodeURIComponent,空格会将 URL 分成两部分,导致请求失败。API 调用错误的 50% 是由于遗漏了这种编码。

🤔 自我解释提示 为什么使用了 geneName.trim()?考虑一下,如果用户在搜索框中输入 "TP53 "(末尾带有空格),如果没有 trim,API 会如何接收。 (与前面的引物部分中对序列进行规范化处理的理念相同——在发送之前统一格式。)


2段階目 — 実際にリクエストを送信する(fetch、完成版)

実際にリクエストを送信し、応答を待つ部分はツールなので、完成版を提供します。ajax-fetchsync-vs-asyncで学んだfetch + async/awaitをそのまま使います。

javascript
async function callGeneApi(geneName) {
  const url = buildQueryUrl(geneName);
  const response = await fetch(url);        // ← ネットワーク往復。待機する(await)
  if (!response.ok) {
    throw new Error(`APIエラー: ${response.status}`);
  }
  return await response.json();             // ← 応答本文をJSONオブジェクトに変換
}

🔎 なぜasync/awaitなのか?(ドロー — sync-vs-async) ネットワークリクエストは時間がかかります(サーバーとの往復)。その間、ブラウザが停止してはいけません。awaitは、「これを完了するまで待つが、その間は画面がフリーズしないようにする」という処理を行います。レストランで注文し、呼び出しベルを受け取って席に座って待つようなものです。料理ができるまで立ちっぱなしで待つ必要はありません。

この関数の最後の行 response.json() が、次の段階に進むための橋渡しとなります。サーバーが送信した文字列の塊(テキスト)を、扱いやすいJavaScriptオブジェクトに変換します。そのオブジェクトを調理するのが3段階です。


ステップ 3:必要なものだけを抽出する ★(JSON)

✍️ 手動で記述する箇所。 部品 = JSON。目的:複雑な応答から、必要な値だけを安全に抽出する。

API が返す JSON は、通常、私たちが望むものよりもはるかに大きく複雑です。実際の応答は、大体以下のようになっているとします。

json
{
  "hits": [
    {
      "symbol": "TP53",
      "name": "tumor protein p53",
      "genomic_pos": { "chr": "17", "start": 7668402, "end": 7687550 },
      "alias": ["p53", "LFS1", "BCC7"],
      "summary": "細胞周期の調節と腫瘍抑制に関与する遺伝子。"
    }
  ]
}

ここで、カードに表示するものは、名前、位置、別名、概要程度です。JSON を調べて、必要な値だけを抽出し、きれいなオブジェクトに整理する関数を作成します。このとき、必ず注意すべきこと — 存在しない可能性のある値を安全に処理することです。一部の遺伝子には別名がなく、一部の応答には結果が全く含まれていない場合があります。

javascript
function parseGeneResponse(data) {
  const hit = data.hits && data.hits[0];
  if (!hit) {
    return null;                          // 結果がない — 呼び出し元で処理
  }
  const pos = hit.genomic_pos || {};
  return {
    symbol: hit.symbol || "?",
    name: hit.name || "",
    location: pos.chr ? `chr${pos.chr}:${pos.start}-${pos.end}` : "情報なし",
    aliases: Array.isArray(hit.alias) ? hit.alias : [],   // ない場合は空の配列
    summary: hit.summary || "概要情報がありません。",
  };
}

次に、モック応答で検証します。実際のネットワークを使用せずに、解析ロジックが正しいかどうかを確認できます。これは優れた設計です — 純粋なデータ処理(解析)をネットワークから分離しておくと、テストが容易になります。

javascript
const mockResponse = {
  hits: [{
    symbol: "TP53",
    name: "tumor protein p53",
    genomic_pos: { chr: "17", start: 7668402, end: 7687550 },
    alias: ["p53", "LFS1", "BCC7"],
    summary: "細胞周期の調節と腫瘍抑制に関与する遺伝子。",
  }],
};

const parsed = parseGeneResponse(mockResponse);
console.assert(parsed.symbol === "TP53", "シンボル解析失敗");
console.assert(parsed.location === "chr17:7668402-7687550", "位置の組み立て失敗");
console.assert(parsed.aliases.length === 3, "別名解析失敗");

// 結果がない応答も安全に処理する必要がある
console.assert(parseGeneResponse({ hits: [] }) === null, "空の結果の処理失敗");
console.assert(parseGeneResponse({}) === null, "空のオブジェクトの処理失敗");

// 別名がない遺伝子でもエラーにならないようにする
const noAlias = parseGeneResponse({ hits: [{ symbol: "X", genomic_pos: { chr: "1", start: 1, end: 2 } }] });
console.assert(Array.isArray(noAlias.aliases) && noAlias.aliases.length === 0, "別名がない場合の処理失敗");

最後の 3 つの assert が、初心者と上級者を区別します。正常な場合だけを処理すれば初心者で、存在しない場合、空の場合、壊れた場合まで処理すれば上級者です。hit.alias が存在しない場合、そのまま使用すると undefined.length でアプリがクラッシュします。Array.isArray(...) ? ... : [] という 1 行のコードが、その事故を防ぎます。

🤔 自己説明プロンプト data.hits && data.hits[0]&& はなぜ使用したのでしょうか?もし data.hits が全く存在しない(undefined)応答が来た場合、この防御がなければどのようなエラーが発生するかを説明してください。(前のプライマーの「存在しない可能性のある値」と同じテーマです。)


部品を一つに — 完成されたウィジェット

URLの組み立て(API)→呼び出し(fetch)→パース(JSON)→レンダリングを繋ぎます。

javascript
function renderGeneCard(gene) {
  if (!gene) return "検索結果がありません。";
  return [
    `${gene.symbol}  (${gene.name})`,
    `📍 位置 : ${gene.location}`,
    `🏷️ 別名 : ${gene.aliases.join(", ") || "なし"}`,
    `🧬 要約 : ${gene.summary}`,
  ].join("\n");
}

async function searchGene(geneName) {
  try {
    const raw = await callGeneApi(geneName);
    const gene = parseGeneResponse(raw);
    return renderGeneCard(gene);
  } catch (e) {
    return `エラーが発生しました: ${e.message}`;
  }
}

// レンダリングロジックもモックで検証(ネットワーク不要)
const card = renderGeneCard(parseGeneResponse(mockResponse));
console.assert(card.includes("TP53"), "カードのレンダリングに失敗");
console.assert(card.includes("chr17:7668402-7687550"), "カードの位置のレンダリングに失敗");
console.assert(renderGeneCard(null) === "検索結果がありません。", "空の結果のレンダリングに失敗");

searchGene全体がtry/catchで囲まれていることに注目してください。ネットワークはいつでも失敗する可能性があります(サーバーダウン、インターネット接続の切断)。その時、アプリ全体が停止するのではなく、「エラーが発生しました」と表示する — これが実用的なツールと玩具の違いです。


別の道もある(マルチパス推論)

  • 複数の遺伝子を同時に検索: 遺伝子を50個、一つずつawaitすると遅くなる(逐次処理)。Promise.all([...])を使って同時に実行すると、はるかに高速になる。トレードオフ: サーバーに負荷をかける可能性があるため、公開APIには1秒あたりのリクエスト数の制限(レート制限)がある。
  • キャッシュ: 同じ遺伝子を再度検索する場合、APIを再度呼び出すのではなく、保存しておいた結果を使用する。前回の辞書(配列→値)と同じ考え方。ここでは遺伝子名→結果
  • バックエンドプロキシ: APIキーが必要なサービスの場合、キーをブラウザに置くと、キーが公開されてしまう。そのため、実務では、サーバーを介してAPIを呼び出す。→応用編「遺伝子検索バックエンド」に続く。

要点: フロントエンドで外部APIを直接呼び出すことは、高速化に役立つが、**速度(同時リクエスト)、再利用(キャッシュ)、セキュリティ(キー)**の3つの要素において、それぞれより良い選択肢が存在する。

次のステップへ(下部のリンク)


実際にやってみよう(独立した課題)

  1. ローディング状態: 検索中に「検索中…」を表示し、完了したら結果に置き換えてください。(非同期の「待ち時間」をUIで表現)
  2. キャッシュの追加: 既に検索した遺伝子がAPIを再度呼び出さないように、オブジェクト({})に保存して再利用してください。console.assertで2回目の呼び出しがキャッシュを使用していることを確認します。
  3. 複数の遺伝子: カンマで区切られた複数の遺伝子名を受け取り、Promise.allを使用して同時に検索します。
  4. チャレンジ: APIの応答形式が変更され、genomic_posが配列(複数の位置)で返される場合があります。parseGeneResponseが配列であろうとオブジェクトであろうと、安全に処理できるように修正してください。

まとめ

「毎回サイトで遺伝子情報を検索する」という手間を、APIを1つ使ってアプリに組み込む検索ウィジェットに置き換えました。

  • APIは、「何をどこに尋ねるか」をURLで表現します。(質問)
  • fetch/非同期は、リクエストを送信し、画面をフリーズさせずに待ちます。(送信)
  • JSONは、複雑なレスポンスから必要な値だけを、存在しない値も含めて安全に取り出します。(解釈)

APIとJSONは、別々に学ぶときは抽象的ですが、組み合わさると世界のすべての公開データベースが、自分のアプリの材料になります。遺伝子データベースであろうと、論文データベースであろうと、タンパク質構造データベースであろうと、パターンは常に同じです。注文URLを作成し、送信し、回答をパースします。

この記事は一般的な教育例です。実際のサービスでは、認証、リクエスト制限、エラー再試行、ページネーションなどが追加されます。その詳細なバージョンは、この基本構造の上にあなたが追加することができます。

💬 質問・コメント

0件のコメント

ログインせずに投稿できます。ゲスト投稿は投稿者自身で編集・削除できません。

0/2000

読み込み中...