Skip to content

Instantly share code, notes, and snippets.

@zaetrik
Created April 19, 2020 10:50
Show Gist options
  • Save zaetrik/fc27714e7be458625306eb18b640ca08 to your computer and use it in GitHub Desktop.
Save zaetrik/fc27714e7be458625306eb18b640ca08 to your computer and use it in GitHub Desktop.
Functional JS API Requests - Complete Example
import { either, taskEither } from "fp-ts";
import { flow } from "fp-ts/lib/function";
import { pipe } from "fp-ts/lib/pipeable";
import axios from "axios";
/**
* In this real life example we fetch data from an API and validate the result with the usage of Either and TaskEither
*/
enum CountryCode {
EUR = "EUR",
USD = "USD",
CAD = "CAD",
HKD = "HKD",
ISK = "ISK",
PHP = "PHP",
DKK = "DKK",
HUF = "HUF",
CZK = "CZK",
GBP = "GBP",
RON = "RON",
SEK = "SEK",
IDR = "IDR",
INR = "INR",
BRL = "BRL",
RUB = "RUB",
HRK = "HRK",
JPY = "JPY",
THB = "THB",
CHF = "CHF",
MYR = "MYR",
BGN = "BGN",
TRY = "TRY",
CNY = "CNY",
NOK = "NOK",
NZD = "NZD",
ZAR = "ZAR",
MXN = "MXN",
SGD = "SGD",
AUD = "AUD",
ILS = "ILS",
KRW = "KRW",
PLN = "PLN",
}
interface ExchangeRates {
rates: {
[key in CountryCode]: number;
};
base: CountryCode;
date: string;
}
// We create a function that could fail & returns a Promise
const fetchExchangeRatesForCountry = async (
cc: CountryCode
): Promise<ExchangeRates> => {
const res = await axios.get(
`https://api.exchangeratesapi.io/latest?base=${cc}`
);
return res.data as ExchangeRates;
};
const validateExchangeRates = (countryCode: CountryCode) => (
response: ExchangeRates
): either.Either<Error, ExchangeRates> => {
return response.base === countryCode
? either.right(response as ExchangeRates)
: either.left(
Error(
`Invalid exchange rates data! Expected ${countryCode} but got ${response.base}`
)
);
};
const getExchangeRatesForCountry = (countryCode: CountryCode) =>
pipe(
// tryCatch transforms our Promise that may reject to a Promise that never rejects and returns an Either instead
taskEither.tryCatch(
// We need an anonymous function here because we can only use tryCatch with functions that return Lazy<Promise<any>>
() => fetchExchangeRatesForCountry(countryCode),
either.toError
),
taskEither.chain(
// flow => function composition (from left to right)
// could also be written without flow() => taskEither.fromEither(validateExchangeRates(countryCode))
// we validate our response
// => returns Either
// => transform Either to TaskEither
flow(validateExchangeRates(countryCode), taskEither.fromEither)
)
)();
getExchangeRatesForCountry(CountryCode.USD).then(console.log);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment