Joel Thoms
joel.net - JavaScript, ReactJS, and Node

joel.net - JavaScript, ReactJS, and Node

⛅ Making API Calls From a Cloudflare Worker (lesson 3)

⛅ Making API Calls From a Cloudflare Worker (lesson 3)

Joel Thoms's photo
Joel Thoms

Published on Dec 8, 2020

7 min read

Subscribe to my newsletter and never miss my upcoming articles

Being able to retrieve data from an API or Database is a common task for a Cloudflare Worker. Without some type of API call to a database or service, your site would be the same as a static site. It's the API call that will make your site dynamic.

Cloudflare Workers run on the Edge (server side), so the resulting output will be server rendered. making your page SEO (Search Engine Optimization) friendly.

Cloudflare Workers Course Outline

  1. Getting Started with Serverless Cloudflare Workers
  2. Cloudflare Workers as a Web Server (with Webpack)
  3. Making API Calls From a Cloudflare Worker
  4. Key-Value Storage With Cloudflare Workers KV
  5. [Bonus] Smart Routing with Cloudflare Workers

Starting Where We Left Off

In case you haven't been following along with the previous lessons (you don't have to), you can start here:

# continue where we left off
$ git clone https://github.com/joelnet/cloudflare-worker-website.git
$ cd cloudflare-worker-website
$ git checkout ae0c7dcf533099832bc313099425d270e326148e
$ npm ci

I would recommend against using a library like axios and instead use the native fetch. The reason is your Worker has 1 MB limit. Adding any new libraries should be carefully considered due to this hard limit.

Cloudflare Worker code runs server side. Thus you don't have to worry about browser incompatibility with libraries.

Test other clients you may be considering. Since Workers do not run Node.js, some npm modules may not work. For example, any library that requires node-gyp will not run in a Worker.

If a library will run in the browser, it should also run in a Cloudflare worker.

Create src/api.js

I like to create an src/api.js file to hold all my API calls. This way if an underlying change needs to happen, it can happen in one file. This file will also simplifies the source for files using the API.

For this demo, I will be making two API calls, fetchAllPeople and fetchPerson.

const getJson = async url => {
  const response = await fetch(url)
  if (!response.ok) {
    throw new Error('Unexpected HTTP Response')
  }
  return await response.json()
}

export const fetchAllPeople = () =>
  getJson('https://swapi.dev/api/people')

export const fetchPerson = id =>
  getJson(`https://swapi.dev/api/people/${id}`)

If you are curious about why I create the getJson abstraction this way and want to know more, I have created a video on this topic.

Create src/pages/allPeople.js

This page will display a list of all available people. The SWAPI API features pagination, which we'll ignore to keep this demo short. Feel free to implement the pagination on your own 👍

import { fetchAllPeople } from '../api'
import { htmlResponse } from '../lib/responses'

const personToLi = (person, index) =>
  `<li><a href="/people/${index + 1}">${person.name}</a></li>`

const allPeople = async () => {
  const { results } = await fetchAllPeople()
  const persons = results.map(personToLi)
  const html = `<ul>${persons.join('')}</ul>`
  return htmlResponse(html)
}
export default allPeople

Create src/pages/person.js

This page will display the individual person. There are more sophisticated ways of pulling the id from the URL, but I'm just going to use substring for simplicity of this demo.

import { fetchPerson } from '../api'
import { objectToTable } from '../lib/objectToTable'
import { htmlResponse } from '../lib/responses'

const person = async request => {
  const url = new URL(request.url)
  const id = url.pathname.substring(8)

  const response = await fetchPerson(id)
  const table = objectToTable(response)
  const html = `${table}
    <a href="/people">&lt;&lt; back</a>
  `

  return htmlResponse(html)
}

export default person

Support files

Create src/lib/objectToTable.js. This will convert an Object to an HTML table for this demo.

I don't care for looking at raw JSON, even if it's just a demo. So I'm gonna pretty it up a bit.

export const objectToTable = obj => {
  const rows = Object.entries(obj)
    .map(([key, value]) => `<tr><th>${key}</th><td>${value}</td></tr>`)
    .join('')
  return `<table>${rows}</table>`
}

Add Pages To The Router

Now I need to add the routes to the src/index.js Router.

Import the new pages at the top of src/index.js:

import allPeople from "./pages/allPeople";
import person from "./pages/person";

Then add the router code for those pages:

router.get('/people/?', () => allPeople(request))
router.get('/people/.+', () => person(request))

Run The Cloudflare Worker Locally

$ npm run preview

After the browser opens, navigate to /people.

image.png

Click on any person to view the /people/{id} route.

image.png

View the source to confirm the application is rendered on the server.

<ul>
  <li><a href="/people/1">Luke Skywalker</a></li>
  <li><a href="/people/2">C-3PO</a></li>
  <li><a href="/people/3">R2-D2</a></li>
  <li><a href="/people/4">Darth Vader</a></li>
  <li><a href="/people/5">Leia Organa</a></li>
  <li><a href="/people/6">Owen Lars</a></li>
  <li><a href="/people/7">Beru Whitesun lars</a></li>
  <li><a href="/people/8">R5-D4</a></li>
  <li><a href="/people/9">Biggs Darklighter</a></li>
  <li><a href="/people/10">Obi-Wan Kenobi</a></li>
</ul>

Summary

Pulling data from an API and rendering the content from the server side is key for application that are require SEO. Cloudflare Workers let's us do this with very little code.

Browse the repository at this point in history.

Subscribe to my Newsletter to continue learning about Cloudflare Workers!

Cheers 🍻

 
Share this