/net

A network communications library/plugin for NGN (primarily HTTP ops).

Primary LanguageJavaScriptMIT LicenseMIT

NGN Network

A plugin for NGN

Live examples on codepen.

The NGN network plugin provides two major building blocks:

  1. HTTP Client
  2. HTTP Resource

The following features are also included:

  1. URL
  2. Fetch
  3. INTERFACES
  4. HOSTNAME

HTTP Client

The HTTP client provides an intuitive way to execute HTTP requests. All major HTTP methods are supported (GET, POST, PUT, DELETE, HEAD, OPTIONS, & TRACE). Two additional "helper" methods exist for accessing JSON data: JSON and JSONP (only used in browser runtimes).

// Browser/Deno Runtime
import NGN from 'https://cdn.jsdelivr.net/npm/ngn'
import { Client } from 'https://cdn.jsdelivr.net/npm/@ngnjs/net'
// Node Runtime
// import NGN from 'ngn'
// import { Client } from '@ngnjs/ngn'

const client = new Client()

client.GET('https://domain.com').then(response => console.log(response.body)).catch(console.error)

client.GET('https://domain.com', function (response) {
  console.log(response)
})

client.JSON('https://domain.com/data.json', data => {
  console.log(data)
})

This client is intentionally simple. It behaves more like an API tool, similar to Postman. Issuing a request will return a response. This differs from some request libraries, which throw errors for certain HTTP status codes, such as 404. These are valid responses. The client treats them as such. The client will only throw an error when a code problem occurs. Developers are free to handle network responses however they see fit.

The client natively supports headers, query strings/parameters, promises, and callbacks. This differs from other libraries, which rely on dependencies. The native capability allows this client to run consistently across runtimes, such as browsers, Node.js, and Deno.

Some runtimes do not support all of the features developers are used to in the browser. For example, the browser Fetch API supports caching, CORS mode, referral policies, subresource integrity, and abort controllers. Node.js is does not natively support most of these, while Deno is missing a few. These features have been stubbed in the client. Using them will not break an application, but they also won't do anything by default. If these features are important in your application, use the @ngnjs/libnet-node library, which polyfills these features for Node.js.

HTTP Resource

An HTTP resource is a special HTTP client. It applies common values to all requests. For example:

const API = new Resource({
  baseUrl: 'https://api.domain.com',
  headers: {
    'x-version': 'v1'
  },
  token: 'mytoken'
})

API.JSON('/endpoint').then(data => { ... }).catch(console.error)

In the example above, a request is made to https://api.domain.com/endpoint. Two headers are applied automatically:

  1. x-version: v1
  2. Authorization: Bearer mytoken.

Any option that can be applied to a Request can be applied to a resource (including query parameters and URL hashes).

Primary Purpose

Resources make it easy to organize communications with remote services. They can be used to create custom API clients, including multiple clients per application.

Extended Configuration Options

Resources also have a few special configuration attributes:

  • nocache - set this to true to avoid caching. This overrides the Cache-Control header.
  • unique - this this to true to automatically append a unique query parameter to all requests (cache busting).
  • useragent - Specify a custom user agent name. This can be helpful when interacting with API's which require an identifying user agent.
  • uniqueagent - set this to true to generate a unique ID to append to the useragent name. This helps guarantee every request comes from a unique user agent name.
  • tokenRenewalNotice - optionally set this to trigger a notification event before an auth token expires (see Auth Tokens).

Auth Tokens

When interacting with API's, it is common for auth tokens to expire after a period of time. The setToken method will accept the expiration date/time, automatically removing the token when it expires.

By default, a token expiration warning event (token.pending.expiration) is triggered 10 seconds before a token expires. The lead time can be modified using the tokenRenewalNotice configuration option.

Token renewal notices were not designed to auto-renew auth tokens. They were designed to notify the application before a token is expired/removed. Applications can use this feature to renew tokens before they expire (or choose not to).

Cryptography

This library can use the NGN cryptography plugin to automate common cryptography functions such as encrypting and signing request bodies/decrypting and verifying response bodies.

For example:

import NGN from 'https://cdn.jsdelivr.net/npm/ngn/index.js'
import 'https://cdn.jsdelivr.net/npm/@ngnjs/crypto/index.js'
import { Resource } from 'https://cdn.jsdelivr.net/npm/@ngnjs/net/index.js'

const API = new Resource({
  baseUrl: 'https://api.domain.com',
  encryptionKey: 'my key',
  signingKey: privateKey
  // encryptAll: true,
  // signAll: true,
  // decryptAll: true,
  // signAll: trus
})

const response = await API.POST('/path/to/endpoint', {
  body: 'my body',
  encrypt: true,
  sign: true
})

The code above produces a POST request with the following headers:

  • content-encoding: aes256gcm
  • content-transfer-encoding: base64
  • content-type: application/octet-stream; charset=UTF-8
  • signature: ATGtLKdTxjhpWjsKqISSaL28+KW+GIurP8xj/LEZz8ju1gxBeJM4qTwFMVfkER0JuFxEKxUoQt+S2zn7tUqa2HalfuQTRuN50JmldT8eHGtjdmBzydCUjzibVNJpUdISjoJaWfRQdCbtvk6/L1T0HR7XV4pyEFF2Nc2Jbep5ef7z5iFd9Z/ai3V8pARj5zGKdQKpgkx165RP3oAV1IVEc5tqCb5x5BzTaGi1DRvRZXmBgQRA05DPXQEMFYp5Nrt/4M0Z7/dZW8jWQkIKbHL5bWhMUndFgIo/6Aqxt2Lw89Tm2K7BebX0arlgTfcLxFR374CrVOH2G2DJovD4DF3d4g==

and a body of:

SGdrAETObPigA5EDWxNXq1zynaAGsrnVCXAKMejoJ1K6cTJ/NBoFb5OykDBxBaV6xyC3

This request contains the appropriate headers and generated a signature for the encrypted content, which can be verified server side before decrypting. It is possible to sign/verify without encrypting/decrypting (and vice versa).

JSON bodies are automatically converted to strings before encryption. Auto-decryption attempts to JSON.parse() the response body, but will not throw an error if it fails to do so.

It is possible to set encryptAll, decryptAll, signAll, and verifyAll on a Resource. If encryptAll is set to true, then all request bodies are encrypted using the encryption key, unless a Request is set to encrypt: false. In other words, it is possible to set a global default in the network Resource, but override it in the network Request.

It is also possible to import specific components of the crypto library if needed. For example:

import NGN from 'https://cdn.jsdelivr.net/npm/ngn/index.js'
import { generateKeys } from 'https://cdn.jsdelivr.net/npm/@ngnjs/crypto/index.js'

const { publicKey, privateKey } = generateKeys()

For details, see libcrypto and the crypto plugin.

URL

The URL class, often aliased as Address, is an enhanced version of the URL API.

Query Parameters

The searchParams feature of the URL API is still available, but sometimes it is just simpler to reference a query object (instead of a map-like object). For this purpose, a query attribute is available, providing a common key/value structure of all the query parameters (readable and writable).

The querystring attribute also provides a convenient way to read/write the full query string.

Ports & Protocols

In the process of manipulating URL's, it is common to need to change the port. This URL class automatically recognizes default ports for several known protocols (http, https, ssh, ldap, sldap, ftp, ftps, sftp). Custom default ports can also be specified using the setDefaultProtocolPort() method. When the protocol is changed, it does not update the port automatically, but the update.protocol event is triggered, allowing applications to determine if the port should be updated.

Setting port to 'default' will always use the default port of the specified protocol. The resetPort() method is a shortcut for doing this.

Cross Origin Features

The local attribute determines whether a URL is local to the system or not. In browsers, this detects whether the URL's origin is the same as the page. In backend runtimes, this is compared to the system hostname (server name), as well as the local IP addresses and any other local interfaces.

The isSameOrigin() method provides a quick and convenient way to determine if a different URL shares the same origin (i.e. not a cross-origin URL). This method optionally supports strict protocol matching.

These features can help assess and minimize CORS errors.

URL Formatting & Output

The URL class has two methods, toString() and formatString.

toString() differs from the URL API. It accepts a configuration argument, allowing certain aspects of the URI to be ignored or forced. For example:

const myUrl = 'http://locahost'

console.log(myUrl.toString({ port: true }))
// Outputs http://localhost:80

The formatString accepts a template, which will be populated with the attributes of the URL. For example:

const myUrl = 'http://jdoe:secret@localhost/path.html'

console.log(myUrl.formatString('{{username}} accessing {{path}}'))
// Outputs jdoe acessing path.html

Additional Features

The Client, Resource, and URL classes all implement an NGN.EventEmitter. This means they'll fire change events.

Additional Installation Options

The CDN is the simplest way to use the library:

import * as NET from 'https://cdn.jsdelivr.net/npm/@ngnjs/net'

Copy/Paste

It may be tempting to copy/paste the source code from the CDN into a file in your local environment. This file alone is not the complete network library. There are two other submodules, which are loaded dynamically. The first is a fetch shim. This shim helps map runtime functions to the fetch API. The second is a Node.js submodule, which is only loaded in Node environments to polyfill fetch (which Node does not have).

The library only attempts to load submodules when needed. If the submodules are not available, errors like this one may appear:

NGN.NET Errors

All of the submodules are available at JSDelivr.com (@ngnjs/net).

Serving Locally

Install the npm module using npm install @ngnjs/net and optionally npm install @ngnjs/net-debug (for sourcemaps).

Known Issues

Fetch API: ReferrerPolicy & Cache

Deno doesn't support fetch ReferrerPolicy & Cache. Deno is working on cache support, but will likely not implement ReferrerPolicy. Node.js doesn't support these either, but the @ngnjs/libnet-node plugin can polyfill such features.

ReferrerPolicy is less likely to be necessary in non-browser environments, with the exception of a limited set of proxy and API applications.

The ReferrerPolicy polyfill in @ngnjs/libnet-node currently uses the os module to identify the server hostname & IP address. This is used to help determine when a referrer is on the same host or not. Hostname/IP recogniition is unavailable prior to and including Deno 1.18.1.