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!