Currying and Partial Application with Async Await and Optional Parameters

May 10, 2020

I recently completed a great introduction to Functional Programming (FP) on egghead.io. One example of a curried function from the course was a simple API call:

const getFromAPI = baseUrl => endpoint => cb =>
  fetch(`${baseUrl}${endpoint}`)
    .then(res => res.json())
    .then(data => cb(data))
    .catch(err => {
      console.error(err.message);
    });

Looks great and works great but I wanted to expand on this example by:

  • Rewriting this using async/await rather than chaining thenable methods
  • Including an optional parameter for endpoints that support something like an ID parameter to get a specific item rather than a list of items

Rewrite using async/await

Here’s what I came up with. It’s a pretty standard syntax for async functions and still allows for partial application. Note that the final function (where we pass the callback) is the only function body that uses the await keyword, so this is the only async function.

const getFromApi = baseUrl => endpoint => async callback => {
  try {
    const response = await fetch(`${baseUrl}/${endpoint}`);
    const data = await response.json();
    callback(data);
  } catch ({ message }) {
    console.error({ message });
  }
};

// usage
const getMyApi = getFromApi("https://jsonplaceholder.typicode.com");

// create separate functions using the same baseUrl with different endpoints
const getUsers = getMyApi("users");
const getTodos = getMyApi("todos");

// create a callback function
const callback = data => console.log(data);

// apply the final parameter to execute the API calls
getUsers(callback); // => logs an array of users
getTodos(callback); // => logs an array of todos

Include an optional parameter

The users and todos endpoints both support getting a single user or todo object by passing an id parameter.

const user3url = "https://jsonplaceholder.typicode.com/users/3";
const todo4url = "https://jsonplaceholder.typicode.com/todos/5";

In order to keep the getFromApi() function reusable and declarative, I need a way to pass an optional id param. I swapped out my endpoint string parameter for an object with keys for endpoint and id. Now we can use this function to get all users, todos, or a specific user or todo by passing an id.

const getFromApi = baseUrl => ({ endpoint, id = null }) => async callback => {
  try {
    const response = await fetch(`${baseUrl}/${endpoint}${id ? `/${id}` : ""}`);
    const data = await response.json();
    callback(data);
  } catch ({ message }) {
    console.error({ message });
  }
};

// usage
const getMyApi = getFromApi("https://jsonplaceholder.typicode.com");

// create separate functions using the same baseUrl with different endpoints
const getUsers = getMyApi({ endpoint: "users", id: 3 });
const getAllUsers = getMyApi({ endpoint: "users" });

// create a callback function
const callback = data => console.log(data);

// apply the final parameter to execute the API calls
getAllUsers(callback); // => logs all users
getUsers(callback); // => logs user where id = 3

Happy Coding!


Mark Barry - Front-end Developer
Orange County, CA.

© 2021