Node API used to export a FoxyApi
class:
// CJS (Node 10-12)
const { FoxyApi } = require("@foxy.io/node-api");
// ESM (TypeScript, Node 13+)
import { FoxyApi } from "@foxy.io/node-api";
const api = new FoxyApi({
clientId: "client_MY-CLIENT-ID",
clientSecret: "long-alphanumeric-client-secret",
refreshToken: "long-alphanumeric-refresh-token",
});
Foxy SDK has a namespace export. You can find a hAPI client under Integration:
// CJS (Node 10-12)
const FoxySDK = require("@foxy.io/sdk");
// ESM (TypeScript, Node 13+)
import * as FoxySDK from "@foxy.io/sdk";
const api = new FoxySDK.Integration.API({
clientId: "client_MY-CLIENT-ID",
clientSecret: "long-alphanumeric-client-secret",
refreshToken: "long-alphanumeric-refresh-token",
});
If you did't provide the full configuration, our hAPI client would look for the missing values in the following env vars:
FOXY_API_CLIENT_ID # config.clientId
FOXY_API_CLIENT_SECRET # config.clientSecret
FOXY_API_REFRESH_TOKEN # config.refreshToken
Foxy SDK doesn't support this out of the box, but you can always use process.env
to achieve the same effect (and you can use your own env vars as well):
const api = new FoxySDK.Integration.API({
clientId: process.env.FOXY_API_CLIENT_ID,
clientSecret: process.env.FOXY_API_CLIENT_SECRET,
refreshToken: process.env.FOXY_API_REFRESH_TOKEN,
});
In Node API package you could set config.cache
to persist credentials between service invocations if it provided better performance for your use case:
const api = new FoxyApi({
cache: new FoxyApi.cache.DiskCache("/tmp/.api_cache"),
});
You can pass any datastore implementing Web Storage API to config.storage
to do the same in our SDK.
import { LocalStorage } from "node-localstorage";
const api = new FoxySDK.Integration.API({
storage: new LocalStorage("/tmp/.sdk_storage"),
});
Please note that we've removed our own DiskCache
, MemoryCache
and MixedCache
in favor of 3rd-party packages such as node-localstorage or fake-storage.
You SHOULD NOT use cache files generated by @foxy.io/node-api
with @foxy.io/sdk
as there may be differences in data schemas.
Node API used Winston for logging, so you could specify options like these to control the output:
const api = new FoxyApi({ logLevel: "debug" });
const api = new FoxyApi({ silent: true });
SDK uses Consola and accepts a numeric config.level
property instead (see available values):
const api = new FoxySDK.Integration.API({ level: 4 });
const api = new FoxySDK.Integration.API({ level: -Infinity });
Node API package had a single .fetch()
method on every resource or collection reference that would return parsed JSON or throw an error if request failed:
const store = await api.follow("fx:store").fetch();
You could also follow and fetch linked resources by calling api.from()
:
const store = await api.from(transaction).follow("fx:store").fetch();
And you could always use .fetchRaw()
for low-level interactions:
const store = await api.fetchRaw({
url: new URL("/stores/8", FoxyApi.endpoint),
});
Foxy SDK, on the other hand, exposes a number of methods for making requests: .get()
, .put()
, .post()
, .patch()
and .delete()
. Each one of those methods returns a Response with status and body accessors:
const response = await api.follow("fx:store").get();
const store = await response.json();
Note: .get()
accepts the same parameters as .fetch()
of the Node API, except for query
– SDK has filters
property for that:
api
.follow("fx:store")
.follow("fx:transactions")
.get({ filters: ["customer_id=123"] });
To access linked resources, simply call one of the methods listed above on the required link the _links
section of the response body obtained with response.json()
:
const response = await transaction._links["fx:store"].get();
const store = await response.json();
For low-level interactions you can use .fetch()
on the api instance – it implements exactly the same interface as the fetch()
method of Fetch API:
const response = await api.fetch(new URL("/stores/8", api.base).toString());
const store = await response.json();
With Node API you could .follow()
a numeric resource ID like this:
const transaction = await api
.follow("fx:store")
.follow("fx:transactions")
.follow(123)
.fetch({ method: "GET" });
API client would use smart resolution to match the curie and the numeric ID to a hardcoded hAPI resource URL, and then make a single request to /transactions/123
.
Even though we tried our best to make smart resolution as compatible as possible with our Hypermedia API, we still couldn't find a good way to make it fail-safe. To ensure the reliability of our SDK, we've decided to remove smart resolution from our API clients.
If you're fetching collections, you don't need to change anything. If you're fetching single resources by ID, you can use filters instead:
const transaction = await api
.follow("fx:store")
.follow("fx:transactions")
.get({ filters: ["id=123"] })
.then((response) => response.json())
.then((json) => json._embedded["fx:transactions"][0]);
And of course you can always fetch a URL if you know it:
const transaction = await api.fetch("/transactions/123").then((response) => response.json());
We use cache to reduce the number of API requests, so if you're making a request to the same endpoint over and over again, our SDK will most likely perform a full API tree traversal only once. An in-memory cache is used by default, but you can also persist it to disk by passing any datastore implementing Web Storage API to config.cache
:
import { LocalStorage } from "node-localstorage";
const api = new FoxySDK.Integration.API({
cache: new LocalStorage("/tmp/.sdk_cache"),
});
If you're using utilities such as FoxySigner
, you'll find them unchanged in FoxySDK.Integration
namespace:
import { FoxySigner } from "@foxy.io/node-api/dist/signer";
const hmacSign = new FoxySigner();
import * as FoxySDK from "@foxy.io/sdk";
const hmacSign = new FoxySDK.Integration.Signer();