Tutorial to Build a REST-driven Search Component
Follow the steps below to build an interactive book search tool on top of the Google Books API.
View a sample of the JSON results you will be using: https://www.googleapis.com/books/v1/volumes?q=spacex
Steps:
Step 1: Create baseline HTML
Using your favorite code editor, create
an HTML file named book-finder.html
with the content below.
book-finder.html
<!doctype html>
<html>
<head>
<meta charset=utf-8>
<title>Book Finder</title>
</head>
<body>
<main>
<h1>Book Finder</h1>
</main>
</body>
</html>
Open book-finder.html
in your web browser to verify it displays properly.
Step 2: Add search component
Just after the h1
header line, insert the HTML for the search
input box and the submit button.
<main>
<h1>Book Finder</h1>
<label>
Search:
<input placeholder="Enter terms" autofocus>
</label>
<button>Find</button>
</main>
Now we need some HTML to display the search results.
Step 3: Add search results component
Just after the search component you added in the previous step, insert a
section
tag to hold a list of books. Then add the static HTML for a single book.
<section class=books>
<div class=book>
<img src=https://dna-engine.org/graphics/sample-book-cover.jpg alt=cover>
<div>
<h2>Title</h2>
<p>Publisher</p>
<i>Price</i>
</div>
</div>
</section>
If you go to your browser and refresh the book-finder.html
page, you'll see
the sample book desperately needs some styling.
Step 4: Style search results
Add the following lines into the <head>
section:
<style>
body { font-family: system-ui, sans-serif; margin: 30px; }
input { font-size: 1.2rem; background-color: aliceblue; }
.book { display: flex; align-items: flex-start; max-width: 400px; background-color: skyblue; padding: 10px; margin: 10px 0px; }
.book img { width: 100px; margin-right: 10px; }
.book h2 { margin: 0px; }
</style>
The book-finder.html
page looks better with a little CSS, but it's still
completely static.
Next we'll turn the static book HTML into a template that can take JSON data.
Step 5: Define book template
Before we convert the static book HTML into a data-driven template, we need to know the structure of the data. Do this by manually examining the results of a sample search.
Below are the pertinent fields:
{
"totalItems": 1306,
"items": [
{
"id": "5VVDAAAAQBAJ",
"volumeInfo": {
"title": "SpaceX",
"publisher": "Springer Science & Business Media",
"imageLinks": {
"thumbnail": "https://books.google.com/books/...",
},
},
"saleInfo": {
"listPrice": {
"amount": 44.99,
},
},
},
],
},
The API returns a single object. The data we want is the list of books, which is the
items
array.
volumeInfo.title
(title)volumeInfo.publisher
(publisher)volumeInfo.imageLinks.thumbnail
(cover)saleInfo.listPrice.amount
(price)
Convert the book HTML into a template by changing the class book
to an ID
(name of the template) and adding the class dna-template
.
Also insert the data fields surrounded by double tildes (~~
) into the desired
spots within the template.
<div id=book class=dna-template>
<img src=~~volumeInfo.imageLinks.thumbnail~~ alt=cover>
<div>
<h2>~~volumeInfo.title~~</h2>
<p>~~volumeInfo.publisher~~</p>
<i>~~saleInfo.listPrice.amount~~</i>
</div>
</div>
Note:
For a production website, set the image src
attribute to
#
and specify the data field in the data-attr-src
attribute.
This approach maintains
valid HTML
and a
build tool
can automatically convert the links to data URLs.
Now it's time to load some libraries that will help us build dynamic features.
Step 6: Load dna-engine and fetch-json
The easiest way to get the necessary CSS and JavaScript libraries is the pull them from a CDN.
Add the following line into the <head>
section:
head
section
<link rel=stylesheet href=https://cdn.jsdelivr.net/npm/dna-engine@3.0/dist/dna-engine.css>
<script src=https://cdn.jsdelivr.net/npm/dna-engine@3.0/dist/dna-engine.min.js></script>
<script src=https://cdn.jsdelivr.net/npm/fetch-json@3.2/dist/fetch-json.min.js></script>
Go to your web browser and reload the book-finder.html
page.
Verify dna-engine loaded properly by going into the
JavaScript console
and entering the command:
dna.info();
Step 7: Wire up search callback
Start by declaring the url
variable and
writing the shell of a function called findBooks()
.
Put the function just before the closing </head>
tag:
<script>
const url = 'https://www.googleapis.com/books/v1/volumes';
const findBooks = (elem) => {
const terms = globalThis.document.querySelector('input').value;
};
</script>
To wire up the function to the "Find" button, we'll use the dna-click
attribute.
The attribute tells dna-engine which function to call when the user clicks the element.
Set the data-on-click
attribute to findBooks
on the
search component button
:
<button data-on-click=findBooks>Find</button>
Verify the click events are firing the callback by viewing the JavaScript console while clicking the "Find" button.
Step 8: Make REST call
Immediately after the console.log
line, insert the following code:
const handleResults = (data) =>
dna.clone('book', data.items, { empty: true, fade: true });
fetchJson.get(url, { q: terms }).then(handleResults);
- Builds the REST URL
- Invokes fetchJson.get() function to call the Google Books API
- Passes the search results data to the templating engine: dna.clone()
The options { empty: true, fade: true }
tell dna-engine to delete any previous
results before displaying the new results and to smoothly fade in the new results.
Go to your web browser and give it a whirl by searching for "laser".
Step 9: Smarten up the search field
Clicking the "Find" button may be simple, but it makes for a cumbersome user experience. We can leverage the smart update feature in dna-engine to replace the button with automatic searching.
Delete the line of HTML for the <button>
, and update the
<input>
tag to be:
<input data-on-smart-update=findBooks
placeholder="Enter terms" autofocus>
dna-engine passes the event target element into the callback.
The callback event now happens on the <input>
element, so we can
delete the old
const terms = globalThis.document.querySelector('input').value;
line and replace terms
with elem.value
.
{ q: elem.value }
Hop back to your browser and verify that search results are returned continuously as you type in search terms.
Conclusion
That's a wrap!
Finished version:
Code
book-finder.html
<!doctype html>
<html>
<head>
<meta charset=utf-8>
<title>Book Finder</title>
<link rel=stylesheet href=https://cdn.jsdelivr.net/npm/dna-engine@3.0/dist/dna-engine.css>
<script src=https://cdn.jsdelivr.net/npm/dna-engine@3.0/dist/dna-engine.min.js></script>
<script src=https://cdn.jsdelivr.net/npm/fetch-json@3.2/dist/fetch-json.min.js></script>
<style>
body { font-family: system-ui; margin: 30px; }
input { font-size: 1.2rem; background-color: aliceblue; }
.book { display: flex; align-items: flex-start; max-width: 400px; background-color: skyblue; padding: 10px; margin: 10px 0px; }
.book img { width: 100px; margin-right: 10px; }
.book h2 { margin: 0px; }
</style>
<script>
const url = 'https://www.googleapis.com/books/v1/volumes';
const findBooks = (elem) => {
const handleResults = (data) =>
dna.clone('book', data.items, { empty: true, fade: true });
fetchJson.get(url, { q: elem.value }).then(handleResults);
};
</script>
</head>
<body>
<main>
<h1>Book Finder</h1>
<label>
Search:
<input data-on-smart-update=findBooks placeholder="Enter terms" autofocus>
</label>
<section class=books>
<div id=book class=dna-template>
<img src=~~volumeInfo.imageLinks.thumbnail~~ alt=cover>
<div>
<h2>~~volumeInfo.title~~</h2>
<p>~~volumeInfo.publisher~~</p>
<i>~~saleInfo.listPrice.amount~~</i>
</div>
</div>
</section>
</main>
</body>
</html>
Keep going
Hungry for more? Try building something using the Google Maps APIs Web Services.
For example, here's the JSON data for the geographic coordinates of Paris: maps.googleapis.com/maps/api/geocode/json?address=Paris
Questions and comments
Tweet your question or comment with #dna-engine or submit an issue on GitHub.