Back to List

Building a Search Interface for Genetic Information: Leveraging APIs and JSON.

Learn how to create a search widget by fetching data from a public gene database API and parsing the JSON response, step by step.

Intermediate
|
90min
|
Verified (2026-07)
Gene search.Representational State Transfer Application Programming InterfaceJavaScript Object NotationretrieveAsynchronous.Gene annotation.
Progress0/19 (0%)

Gene Information Search Engine โ€” Creating a Search Box Using APIs and JSON

After completing this topic

You will be able to create your own search widget that takes a gene name as input and displays the function, location, and aliases retrieved from a public database in a card format, by combining the API and JSON concepts learned in the textbook. This will give you practical experience with the code required to "use a massive database created by others in your app."

This article is an educational, general example. It uses a publicly available, free gene annotation API as a basis to learn the universal pattern of fetching external data from the web.

"What was that gene again?" โ€” The problem of constantly searching through websites

When experimenting, you often know the name of a gene but forget the details. Which chromosome is TP53 on? What is the official alias of BRCA1?

Each time, we open a browser, go to a public database website, type it into the search bar, and browse the results page. It's okay once or twice, but what if you need to check 50 genes? A hellish cycle of opening and closing 50 tabs begins.

This is where a developer's thought process kicks in: "That website ultimately retrieves data from some database and displays it. Can I directly query that database?" Yes, you can. That's what an API is for.


Let's Look at the Final Product First (Run the Black Box First)

Here's how the widget we're going to build will work: when you type a gene name in the search bar, it will display something like this:

text
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚  ๐Ÿ”  [ TP53            ]  [Search]                     โ”‚
โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
โ”‚  TP53   (tumor protein p53)                            โ”‚
โ”‚  โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€                         โ”‚
โ”‚  ๐Ÿ“ Location : 17p13.1                                 โ”‚
โ”‚  ๐Ÿท๏ธ Aliases : p53, LFS1, BCC7                          โ”‚
โ”‚  ๐Ÿงฌ Summary : Cell cycle regulation, tumor suppression โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

You can get information with just one search bar, without even opening a website. All you need to create this are two concepts: where to ask for the information (API) and how to read the answer (JSON).


What Components Does This Tool Consist Of? (Component Breakdown)

text
Gene Information Retrieval Widget
   โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
   โ”‚  [Question] Request URL Assembly โ”€โ”€โ”€โ”€ Component: API      โ”‚  โ† We create this ourselves โ˜…
   โ”‚              โ”‚                                            โ”‚
   โ”‚              โ–ผ                                            โ”‚
   โ”‚  [Send] Called using fetch โ”€โ”€โ”€โ”€โ”€ Component: fetch         โ”‚  โ† Provided as a completed tool
   โ”‚              โ”‚            + Asynchronous                  โ”‚
   โ”‚              โ–ผ                                            โ”‚
   โ”‚  [Parse] Response Parsing โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ Component: JSON        โ”‚  โ† We create this ourselves โ˜…
   โ”‚              โ”‚                                            โ”‚
   โ”‚              โ–ผ                                            โ”‚
   โ”‚  [Output] Card Rendering                                  โ”‚
   โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
ComponentWhere We Learned ItWhat It Does in This Tool
APIapi-basicsCreates the URL for "what and where to ask"
fetch/Asynchronousajax-fetch, sync-vs-asyncActually sends the request and waits
JSONjson-data-formatExtracts the necessary values from the returned response

๐Ÿ“Œ If You're Seeing These Concepts for the First Time (Links at the Top)

The two new concepts we create ourselves are API and JSON. The fetch tool, which handles actual communication, is provided as a completed tool.

๐Ÿ”Ž API in One Sentence (Drawer โ€” api-basics) An API is like a restaurant menu. You can't go directly into the kitchen (database), but if you order in a way specified on the menu (API), you'll get a dish (data). We learn how to order "give me this gene information" according to the rules of the menu.

Step 1: Constructing the URL for What to Query โ˜… (API)

โœ๏ธ This is where you fill in the details. Component = API. Goal: To create a URL that the API can understand from the phrase "Give me this gene information."

The typical way to order from an API is through a URL. You can create it by appending "address + what you want" according to the rules. For example, suppose a public gene API has the following rules:

text
https://example-api.org/query?q={gene_name}&fields={desired_items}
  • After q=, place the search term (gene name).
  • After fields=, place the items you want to receive, separated by commas.

If you create this manually, there's a risk of typos and encoding errors. Therefore, we create a function to safely construct the URL. A key point here is encodeURIComponent โ€“ it wraps the search term so that the URL doesn't break even if it contains spaces or special characters.

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}`;
}

// Verification
console.assert(
  buildQueryUrl("TP53") === "https://example-gene-api.org/query?q=TP53&fields=symbol%2Cname%2Cgenomic_pos%2Calias%2Csummary",
  "Failed to construct the basic URL"
);
// Search terms with spaces should also be safely encoded
console.assert(
  buildQueryUrl("  TP 53  ").includes("q=TP%2053"),
  "Failed to encode spaces"
);

The second assert is crucial. Even if the user enters a search term with spaces, such as TP 53, it is safely encoded as TP%2053. If you don't use encodeURIComponent, these spaces will break the URL and cause the request to fail. Half of API call errors occur because this encoding is omitted.

๐Ÿค” Self-explanatory prompt Why did we include geneName.trim()? Consider what would happen if the user entered "TP53 " (with a space at the end) in the search box, and how the API would receive it if trim were not present. (This is the same principle as normalizing the sequence in the previous primer section โ€“ aligning the format before sending.)

Step 2: Making it Real โ€” Sending the Request (Fetch, Code Provided)

The part where we actually send the request and wait for the response is provided as a complete tool. It uses the fetch and async/await that you learned in ajax-fetch and sync-vs-async.

javascript
async function callGeneApi(geneName) {
  const url = buildQueryUrl(geneName);
  const response = await fetch(url);        // โ† Network round trip. We wait (await).
  if (!response.ok) {
    throw new Error(`API error: ${response.status}`);
  }
  return await response.json();             // โ† Converts the response body to a JSON object.
}

๐Ÿ”Ž Why async/await? (Drawer โ€” sync-vs-async) Network requests take time (server round trip). We don't want the browser to freeze during that time. await tells the browser to "wait for this to finish, but don't freeze the screen in the meantime." It's like ordering food at a restaurant and getting a buzzer so you can sit down โ€” you don't have to stand there waiting for the food.

The last line of this function, response.json(), is the bridge to the next step. It converts the text sent by the server into a JavaScript object that we can work with. The next step is to process that object.


Step 3: Extract Only the Necessary Data from the Response (JSON)

โœ๏ธ Hands-on section. Component = JSON. Goal: Safely extract the values we need from a complex-looking response.

The JSON response returned by the API is usually much larger and more complex than what we need. Let's assume a typical response looks like this:

json
{
  "hits": [
    {
      "symbol": "TP53",
      "name": "tumor protein p53",
      "genomic_pos": { "chr": "17", "start": 7668402, "end": 7687550 },
      "alias": ["p53", "LFS1", "BCC7"],
      "summary": "A gene involved in cell cycle regulation and tumor suppression."
    }
  ]
}

We only need the name, location, aliases, and summary for the card. Create a function that parses the JSON and extracts only the necessary values into a clean object. Importantly, make sure to handle cases where values might be missing. Some genes may not have aliases, and some responses might not have any results at all.

javascript
function parseGeneResponse(data) {
  const hit = data.hits && data.hits[0];
  if (!hit) {
    return null;                          // No results - caller handles it
  }
  const pos = hit.genomic_pos || {};
  return {
    symbol: hit.symbol || "?",
    name: hit.name || "",
    location: pos.chr ? `chr${pos.chr}:${pos.start}-${pos.end}` : "Information not available",
    aliases: Array.isArray(hit.alias) ? hit.alias : [],   // Empty array if missing
    summary: hit.summary || "No summary information available.",
  };
}

Now, validate with a mock response. This allows you to check if the parsing logic is correct without an actual network connection. This is good design - separating pure data processing (parsing) from the network makes testing easier.

javascript
const mockResponse = {
  hits: [{
    symbol: "TP53",
    name: "tumor protein p53",
    genomic_pos: { chr: "17", start: 7668402, end: 7687550 },
    alias: ["p53", "LFS1", "BCC7"],
    summary: "A gene involved in cell cycle regulation and tumor suppression.",
  }],
};

const parsed = parseGeneResponse(mockResponse);
console.assert(parsed.symbol === "TP53", "Symbol parsing failed");
console.assert(parsed.location === "chr17:7668402-7687550", "Location assembly failed");
console.assert(parsed.aliases.length === 3, "Alias parsing failed");

// Should also handle responses with no results safely
console.assert(parseGeneResponse({ hits: [] }) === null, "Empty result handling failed");
console.assert(parseGeneResponse({}) === null, "Empty object handling failed");

// Should also handle genes with no aliases without crashing
const noAlias = parseGeneResponse({ hits: [{ symbol: "X", genomic_pos: { chr: "1", start: 1, end: 2 } }] });
console.assert(Array.isArray(noAlias.aliases) && noAlias.aliases.length === 0, "No aliases handling failed");

The last three assert statements separate the beginners from the experts. If you only handle the successful case, you're a beginner. If you handle cases where data is missing, empty, or broken, you're an expert. If you just use hit.alias and it's missing, your app will crash with undefined.length. The line Array.isArray(...) ? ... : [] prevents that.

๐Ÿค” Self-Explanatory Prompt In data.hits && data.hits[0], why is && used? If a response comes back where data.hits is completely missing (undefined), explain what error would occur without this safeguard. (This is the same theme as "Values That Might Be Missing" from the previous primer.)

Combining the Pieces โ€“ A Complete Widget

Now, we connect URL construction (API) โ†’ fetching โ†’ parsing (JSON) โ†’ rendering.

javascript
function renderGeneCard(gene) {
  if (!gene) return "No results found.";
  return [
    `${gene.symbol}  (${gene.name})`,
    `๐Ÿ“ Location: ${gene.location}`,
    `๐Ÿท๏ธ Aliases: ${gene.aliases.join(", ") || "None"}`,
    `๐Ÿงฌ Summary: ${gene.summary}`,
  ].join("\n");
}

async function searchGene(geneName) {
  try {
    const raw = await callGeneApi(geneName);
    const gene = parseGeneResponse(raw);
    return renderGeneCard(gene);
  } catch (e) {
    return `An error occurred: ${e.message}`;
  }
}

// Also validate the rendering logic with a mock (no network needed)
const card = renderGeneCard(parseGeneResponse(mockResponse));
console.assert(card.includes("TP53"), "Card rendering failed");
console.assert(card.includes("chr17:7668402-7687550"), "Card location rendering failed");
console.assert(renderGeneCard(null) === "No results found.", "Empty result rendering failed");

Notice how searchGene is wrapped in a try/catch block. Networks can fail at any time (server down, internet disconnected). Instead of the entire app crashing, showing "An error occurred" โ€“ this is the difference between a production-ready tool and a toy.


There are Other Ways (Multipath Reflection)

  • Simultaneous Search of Multiple Genes: Searching for 50 genes one by one using await is slow (sequential). Sending them simultaneously using Promise.all([...]) is much faster. Trade-off: This can put a burden on the server, and public APIs often have a rate limit (requests per second).
  • Caching: If the same gene is searched again, don't call the API again; use the stored result. This is the same idea as the dictionary (sequence โ†’ value) from the previous section โ€” here, it's gene name โ†’ result.
  • Backend Proxy: If the service requires an API key, placing the key in the browser exposes it. In practice, we use our server as an intermediary. โ†’ This leads to the application section Gene Search Backend.

Key Point: Calling external APIs directly from the front-end is good for quick development, but there are better choices in terms of speed (simultaneous requests), reusability (caching), and security (key).

Next Steps (Links at the Bottom)

  • To store search results in our database and retrieve them quickly, โ†’ see the application section Sequence DB Indexing, which uses DB Index.
  • If you want to push results in real-time, โ†’ see WebSocket.
  • The root of reusing the same result (caching) โ†’ List and Dictionary.

Try It Yourself (Independent Exercise)

  1. Loading State: When searching, display "Searching..." and replace it with the results when finished. (Visually represent the asynchronous "waiting time" in the UI.)
  2. Add Cache: Store previously searched genes in an object ({}) to avoid re-calling the API and reuse the results. Use console.assert to verify that the second call uses the cache.
  3. Multiple Genes: Accept multiple gene names separated by commas and search them concurrently using Promise.all.
  4. Challenge: The API response format has changed, and genomic_pos may now be an array (multiple positions). Modify parseGeneResponse to handle both arrays and objects safely.

Summary

We transformed the tedious process of "repeatedly searching for genetic information on a website" into a search widget that brings all the information into your app with a single API.

  • The API represents "what to ask and where to ask it" as a URL. (Question)
  • fetch/asynchronous operations send the request and wait without freezing the screen. (Transmission)
  • JSON extracts only the necessary values from complex responses and handles missing values safely. (Interpretation)

APIs and JSON may seem abstract when learned separately, but when combined, all of the world's public databases become ingredients for your app. Whether it's a gene database, a research paper database, or a protein structure database, the pattern is always the same: create an order URL, send it, and parse the answer.

This article is a general educational example. In a real-world service, authentication, request limits, error retries, and pagination would be added. You can build upon this basic structure to create a more detailed version.

๐Ÿ’ฌ Questions & Comments

0 comments

You can post without signing in. Guest comments cannot be edited or deleted by their author.

0/2000

Loading...