npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

@adobe/cloud-service-client

v2.0.0

Published

Client for working with an HTTP-based cloud service

Downloads

2,312

Readme

Node.JS Cloud Service Client

build status Node version

This client can be used to submit HTTP requests in javascript. You may ask, "why use this client instead of one of the more popular libraries?" First off, it's not a rewrite of HTTP functionality for Node.JS; it's still intended for use with popular modules (like fetch or axios) for the actual HTTP handling. Think of the client more like added functionality built on top of the basic HTTP capabilities that these modules provide; these features are commonly required when communicating with a complex cloud service. Here is some of this functionality:

  • Provides configurable request retry functionality. For example, if an HTTP request fails with a 5xx level error or a network-related error, the client will automatically retry the request up to a given number of times. Each retry will wait exponentially longer between each attempt.
  • Provides configurable "polling" functionality. Even on successful requests, the client can be configured to continually poll a URL until it responds in a desired way.
  • The client is interchangable with axios or fetch. Currently using fetch and need to take advantage of the client's retry functionality? Simply replace your fetch calls with the client; everything else (such as response format, options, etc.) will fit into place seamlessly.
  • Can be configured to handle Set-Cookie header when provided by servers. The client will store these cookies in its own jar and use them in subsequent requests.

Contents

Installation

The module can be installed from npm:

npm install @adobe/cloud-service-client

Usage

Following are some example of how to use the client.

Axios Usage

const originalAxios = require('axios');
const { axiosClient } = require('@adobe/cloud-service-client');

// add client functionality to axios
const axios = axiosClient(originalAxios);

// submit a request that will behave like axios:
const response = await axios('http://myrequesturl.com');

// the response will follow axios' response schema:
const {
  data,
  status,
  statusText,
  headers
} = response;

// axios' other methods work as well:
const response = await axios.get('http://myrequesturl.com);

// pass in any of axios' options:
const response = await axios({
  url: 'http://myrequesturl.com',
  method: 'get',
  responseType: 'stream'
});

Fetch Usage

const nodeFetch = require('node-fetch');
const { fetchClient } = require('@adobe/cloud-service-client');

// add client functionality to node-fetch
const fetch = fetchClient(nodeFetch);

// submit the request as fetch would
const response = await fetch('http://myrequesturl.com');

// the response will follow fetch's schema:
const {
  status,
  statusText,
  headers
} = response;
const header1 = headers.get('my-header);
const data = await response.json();

if (response.ok) {
  // process successful response
}

// pass in any of fetch's options:
const response = await fetch('http://myrequesturl.com', {
  method: 'get',
  mode: 'cors',
  redirect: 'follow'
});

Additional Exports

In addition to providing exports identical to whichever HTTP library is being used, the client provides the following functionality:

  • {function} clearCookies: When invoked, clears any cookies that the client may currently be storing. The method returns a Promise that will resolve when the client's cookies have been cleared.
    • Example: const { clearCookies } = require('@adobe/cloud-service-client');
  • {function} setGlobalOptions: Takes a single argument that should be any client-specific request options to apply to all requests that the client sends. Note that this only applies to the client's request options as described in Request Options; options should be provided to the function as they would to the cloudClient property. The underlying HTTP library of choice (i.e. axios or fetch) should be used to apply other global request-level options.
    • Example: const { setGlobalOptions } = require('@adobe/cloud-service-client');
    • Usage: setGlobalOptions({ eventuallyConsistentCreate: true });

Options

Client Options

Exports like axiosClient() and fetchClient() accept an optional second argument, which should be a simple object containing any of the following configurations:

  • {boolean} handleCookies: If true, the client will use its cookie processing capabilities to provide the cookie handling functionality described in the "Cookies" section. Default: false.
  • {Object} log: See Logging section for more details.

For example:

const axios = axiosClient(originalAxios, {
  handleCookies: true,
});

Request Options

In addition to any options supported by axios or fetch, the client has some request options of its own. These options should all be provided in an cloudClient property on the request's configuration. For example:

fetch(url, {
  method: 'POST',
  mode: 'cors',
  cloudClient: {
    timeout: 10000,
    eventuallyConsistentCreate: true
  }
});
  • {number} timeout: The amount of time, in milliseconds, that the client will wait for a request before aborting. By default, an aborted request qualifies for a retry, so timed out requests will fit in with the client's retry functionality.
  • {object} retry: Various information about how the client will retry requests under certain circumstances.
    • {Array} strategies: The retry "strategies" that the client will send a response through before determining whether the corresponding request needs to be retried. See the "retry" section for more information and examples. Note that these strategies will be in addition to the client's default strategies, which will retry on unexpected network-related errors or 5xx level status codes. Each item in the array should be a strategy object consisting of the following properties:
      • {function} shouldRetry: Should return a Promise that resolves to true if the information provided by the client warrants a retry. The function will be passed a single argument: an object consisting of the following properties:
        • {number} attempts: The number of times the current request has been sent.
        • {number} maxAttempts: The default maximum number of attempts as provided in the client's options.
        • {number} delay: The default time, in milliseconds, that the client should delay between retries.
        • {number} delayMultiple: The default multiple as provided in the client's options.
        • {*} response: Response that was the result of the request. This will be the raw response from the underlying HTTP library.
        • {string} url: URL to which the client sent the HTTP request that generated the response.
        • {object} options: Simple object containing the raw options that were given to the underlying HTTP library.
      • {function} getDelay: Should return a Promise that resolves with the amount of time, in milliseconds, that the client should wait before retrying the request. This value will be used in conjunction with the delay multiple to exponentially delay subsequent retries. The function will be called with a single argument: an object matching the one described in the shouldRetry function. If not specified, the value will default to the delay provided in the client's retry options.
      • {function} getDelayMultiple: Should return a Promise that resolves to the multiple to use when calculating the amount of time to delay before retrying the request. The function will be called with a single argument: an object matching the object described in the shouldRetry function. If not specified the value will default to the multiple provided in the client's retry options.
      • {function} getMaxRetries: Should return a Promise that resolves to the maximum number of times that a given request should be made. The function will be called with a single argument: an object matching the object described in the shouldRetry function. If not specified, the value will default to the number provided in the client's retry options. Note that -1 indicates the client should continue to retry indefinitely; use this option with extreme care.
      • {function} getRequestOptions: Should return a Promise that resolves with the raw options to provide to the underlying HTTP client on the next retry request. These options will be merged with the options that originally generated the response. The function will be called with a single argument: an object matching the one described in the shouldRetry function. If not specified, the client will use the originally provided request options.
    • {number} count: The maximum number of times the client will retry a given request. Note that this is a default value and will not necessarily be respected by all retry strategies. Default: 3.
    • {number} delay: The amount of time, in milliseconds, the client will wait before retrying a request when needed. Default: 1000.
    • {number} delayMultiple: Each time the client delays a retry, it will multiply the delay amount by this value. Assume delay is 1000, retry is 4, and delayMultiple is 2. The first time the client retries a request it will wait 1000ms, the second time it will wait 2000ms, the third time it will wait 4000ms, etc. Note that this is a default value and will not necessarily be respected by all retry strategies. Default: 2.
  • {boolean} eventuallyConsistentCreate: When true, the client will use a built-in retry strategy for handling creation in an eventually consistent system. See the "Retry" section for more details. Default: false.
  • {boolean} eventuallyConsistentUpdate: When true, the client will use a built-in retry strategy for handling updates in an eventually consistent system. See the "Retry" section for more details. Default: false.
  • {boolean} eventuallyConsistentDelete: When true, the client will use a built-in retry strategy for handling deletions in an eventually consistent system. See the "Retry" section for more details. Default: false.

Response

The client will extend the HTTP response provided by the underlying HTTP library with an cloudClient property. Additional information available on this property is as follows:

  • {number} status: Status code of the response. Note that this value may be missing if the request did not generate a response (such as timeout errors or general network-related errors).
  • {number} statusText: Status text of the response. Note that this value may be missing if the request did not generate a response (such as timeout errors or general network-related errors).
  • {number} requestTime: The amount of time, in milliseconds, that the underlying HTTP library took submitting the request and receiving a response. Note that this will apply to last request that was sent. The value may be missing if the request's time could not be determined.
  • {object} headers: Simple object whose keys are header names and values are header values. These are the HTTP headers that were provided in the response.
  • {object} options: The options that were provided to the client when initiating the request. The object will have an cloudClient property (regardless of whether one was initially provided) with the following additional properties added to it:
    • {Array<object>} retryResponses: If the request was retried, a list of each response, in order, that resulted in a retry. Each item in the list will include all the items included in this section for the response that was retried.
    • {number} retries: The number of retries that were made for before providing a response.
    • {number} retryWait: The amount of time, in milliseconds, that the client spent waiting between retries before providing a response.
    • {number} startTime: Unix timestamp of the time when the request was initiated.
    • {number} endTime: Unix timestamp of the time when the client recieved a response.
  • {object} error: Simple object containing information about the error that the underlying HTTP library may have provided. This property will only be present if there was an error, and its contents may vary depending on the error that was thrown. If the error is a known javascript error type, it contain the following properties:
    • {string} name: The name of the error.
    • {string} message: Message that was associated with the error.

Request Retry

The client has built-in support for retrying a given request under certain circumstances. These circumstances are determined by "request retry strategies." In concept, the client has a list of strategies; each strategy is responsible for examining the response to a request and indicating whether the request qualifies for a retry. Whenever the client makes a request, it will pass the response through its list of strategies. The first strategy that indicates the request should be retried will force the client to retry the request. The client will use this algorithm regardless of whether the response can be considered a "success" or a "failure."

By default the client will retry a request up to a maximum number of times, exponentially delaying the amount of time between each retry. However, this behavior can be overridden by individual strategies.

Retry-After Header

If a server responds with a Retry-After header, the client will use that value above all other retry options. For example, assume the client has been configured to delay 1 second between retries, with a delay multiple of 2; when a server responds with a Retry-After value of 5 on the second retry, the client will ignore the delay and delay multiple and wait 5 seconds.

This can still be overridden through the shouldRetry method, which will receive the server's Retry-After value as the default delay.

On subsequent retries, the client will fall back to its default behavior if the server does not provide additional Retry-After headers.

Example Retry Strategies

The following client will retry a request up to 5 times if the response code is 404:

axios({
  url: 'http://myurltorequest',
  cloudClient: {
    retry: {
      count: 4,
      strategies: [{
        shouldRetry: async ({ response }) => response.status === 404
      }]
    }
  }
});

This client will poll a URL for as long as the response code is 202, waiting the same amount of time between each request:

axios({
  url: 'http://myurltorequest',
  cloudClient: {
    retry: {
      strategies: [{
        shouldRetry: async ({ response }) => response.status === 202,
        getDelayMultiple: async () => 1,
        getMaxRetries: async () => -1,
      }]
    }
  }
});

(Be very careful with a setup like this, because the client will poll indefinitely)

Built-in Retry Strategies

By default the client will always retry requests that fail because of network-related issues, or that have a 5xx level response code.

In addition, the client provides the following optional strategies. Note that "Eventually Consistent" refers to the concept where there may be a delay between when a remote system is modified, and when the said modification is reflected in the system.

  • Eventually Consistent Create
    • Uses the default retry, delay, and multiple to retry a request if the response code is 404.
  • Eventually Consistent Update
    • Uses the default retry, delay, and multiple to retry a request if the response code is 200 but the response etag header doesn't match the if-match header provided in the request.
  • Eventually Consistent Delete
    • Uses the default retry, delay, and multiple to retry a request if the response code is not 404.

See the retry options for how to utilize these strategies.

Cookies

If configured to do so, the client has some basic cookie handling in place. For example, if a response handled by the client has a Set-Cookie header, the client will cache those cookies and include them in any subsequent requests that it sends. Note that the cookies are scoped to the lifetime of the client instance - they're not persisted and will be lost when the client goes out of scope.

The client will favor cookies passed in a request's options over any cookies it has stored. For example, assume that the client is storing cookie mycookie with a value of set-cookie-value because it received it in a Set-Cookie header. Also assume that a consumer of the client passes the same cookie in the Cookie header with a value of options-cookie-value when initiating a request. In this case the client will use options-cookie-value as the value of mycookie, because it was passed into the request options.

Cookies specified with Set-Cookie will follow the HTTP specification for the header, and will honor attributes such as Path, Domain, Max-Age, Expires, Secure, etc. The main deviance is that the life of the cookie will always be, at maximum, the client's scope.

This functionality will only be enabled when the handleCookies options is true.

Logging

The client will log messages at different levels throughout the request process. By default none of these messages will appear anywhere; change that behavior by using one of the following methods.

Set a Log Level

Set an environment variable named NODE_HTTP_CLIENT_LOG_LEVEL to the level of logging that is desired. Valid values are DEBUG, INFO, WARN, or ERROR.

When utilizing this logging mechanism, all messages are written to console.log(). Use the custom logger method to change that behavior.

Provide a Custom Logger

Use the log option to provide any logger of your choosing:

const axiosOriginal = require('axios');
const { axiosClient } = require('@adobe/cloud-service-client');

const axios = axiosClient(axiosOriginal, {
  log: {
    debug: () => {},
    info: () => {},
    warn: () => {},
    error: () => {},
  }
});

const response = await axios('http://myrequesturl.com',);

As illustrated in the example, the logger provided must have the following methods:

  • debug()
  • info()
  • warn()
  • error()

Each method is used by the client to log a message at its corresponding log level. The method will receive multiple arguments, similar in nature to how console.log() might be used. Here are some potential examples:

log.debug('simple message');
log.info('formatted message with value %s', formatValue);
log.error('non-formatted message with value', someValue);

Examples

See the library's end-to-end contract tests for various examples of how to use the library.

Releasing

This module uses semantic-release when publishing new versions. The process is initiated upon merging commits to the main branch. Review semantic-release's documentation for full details, but the most commonly used conventions are:

  • feat: for non-breaking releases containing new features. This will increment the minor version number. Example:
    • feat: <message describing changes>
    • Assuming the library's current version is 1.2.1, a commit with a message similar to the above would trigger a new release whose version number would be 1.3.0.
  • fix: for non-breaking releases containing bug fixes. This will increment the build version number. Example:
    • fix: <message describing changes>
    • Assuming the library's current version is 1.2.1, a commit with a message similar to the above would trigger a new release whose version number would be 1.2.2.

PRs whose messages do not meet semantic-release's format will not generate a new release.

Release notes are generated based on git commit messages. Release notes will appear in CHANGELOG.md.

Contributing

Contributions are welcomed! Read the Contributing Guide for more information.

Licensing

This project is licensed under the Apache V2 License. See LICENSE for more information.