Globals

The primary way that JavaScript code can interact with Harper is through the global variables, which has several objects and classes that provide access to the tables, server hooks, and resources that Harper provides for building applications. As global variables, these can be directly accessed in any module.

These global variables are also available through the harperdb module/package, which can provide better typing in TypeScript. To use this with your own directory, make sure you link the package to your current harperdb installation:

npm link harperdb

The harperdb package is automatically linked for all installed components. Once linked, if you are using EcmaScript module syntax you can import function from harperdb like:

import { tables, Resource } from 'harperdb';

Or if you are using CommonJS format for your modules:

const { tables, Resource } = require('harperdb');

The global variables include:

tables

This is an object that holds all the tables for the default database (called data) as properties. Each of these property values is a table class that subclasses the Resource interface and provides access to the table through the Resource interface. For example, you can get a record from a table (in the default database) called 'my-table' with:

import { tables } from 'harperdb';
const { MyTable } = tables;
async function getRecord() {
	let record = await MyTable.get(recordId);
}

It is recommended that you define a database for all the tables that are required to exist in your application. This will ensure that the tables exist on the tables object. Also note that the property names follow a CamelCase convention for use in JavaScript and in the GraphQL Schemas, but these are translated to snake_case for the actual table names, and converted back to CamelCase when added to the tables object.

databases

This is an object that holds all the databases in Harper, and can be used to explicitly access a table by database name. Each database will be a property on this object, each of these property values will be an object with the set of all tables in that database. The default database, databases.data should equal the tables export. For example, if you want to access the "dog" table in the "dev" database, you could do so:

import { databases } from 'harperdb';
const { Dog } = databases.dev;

Resource

This is the base class for all resources, including tables and external data sources. This is provided so that you can extend it to implement custom data source providers. See the Resource API documentation for more details about implementing a Resource class.

auth(username, password?): Promise<User>

This returns the user object with permissions/authorization information based on the provided username. If a password is provided, the password will be verified before returning the user object (if the password is incorrect, an error will be thrown).

logger

This provides methods trace, debug, info, warn, error, fatal, and notify for logging. See the logging documentation for more information.

server

The server global object provides a number of functions and objects to interact with Harper's HTTP, networking, and authentication services.

server.http(listener: RequestListener, options: HttpOptions): HttpServer[]

Alias: server.request

Add a handler method to the HTTP server request listener middleware chain.

Returns an array of server instances based on the specified options.port and options.securePort.

Example:

server.http((request, next) => {
	return request.url === '/graphql'
		? handleGraphQLRequest(request)
		: next(request);
}, {
	runFirst: true, // run this handler first
});

RequestListener

Type: (request: Request, next: RequestListener) => Promise<Response>

The HTTP request listener to be added to the middleware chain. To continue chain execution pass the request to the next function such as return next(request);.

Request and Response

The Request and Response classes are based on the WHATWG APIs for the Request and Response classes. Requests and responses are based on these standard-based APIs to facilitate reuse with modern web code. While Node.js' HTTP APIs are powerful low-level APIs, the Request/Response APIs provide excellent composability characteristics, well suited for layered middleware and for clean mapping to RESTful method handlers with promise-based responses, as well as interoperability with other standards-based APIs like streams used with Blobs. However, the Harper implementation of these classes is not a direct implementation of the WHATWG APIs, but implements additional/distinct properties for the the Harper server environment:

Request

A Request object is passed to the direct static REST handlers, and preserved as the context for instance methods, and has the following properties:

  • url - This is the request target, which is the portion of the URL that was received by the server. If a client sends a request to http://example.com:8080/path?query=string, the actual received request is GET /path?query=string and the url property will be /path?query=string.

  • method - This is the HTTP method of the request. This is a string like GET, POST, PUT, DELETE, etc.

  • headers - This is a Headers object that contains the headers of the request.

  • pathname - This is the path portion of the URL, without the query string. For example, if the URL is /path?query=string, the pathname will be /path.

  • protocol - This is the protocol of the request, like http or https.

  • data - This is the deserialized body of the request (based on the type of data specified by Content-Type header).

  • ip - This is the remote IP address of the client that made the request (or the remote IP address of the last proxy to connect to Harper).

  • host - This is the host of the request, like example.com.

  • sendEarlyHints(link: string, headers?: object): void - This method sends an early hints response to the client, prior to actually returning a response. This is useful for sending a link header to the client to indicate that another resource should be preloaded. The headers argument can be used to send additional headers with the early hints response, in addition to the link. This is generally most helpful in a cache resolution function, where you can send hints if the data is not in the cache and is resolving from an origin:

class Origin {
	async get(request) {
		// if we are fetching data from origin, send early hints
		this.getContext().requestContext.sendEarlyHints('<link rel="preload" href="/my-resource" as="fetch">');
		let response = await fetch(request);
		...
	}
}
Cache.sourcedFrom(Origin);
  • _nodeRequest - This is the underlying Node.js http.IncomingMessage object. This can be used to access the raw request data, such as the raw headers, raw body, etc. However, this is discouraged and should be used with caution since it will likely break any other server handlers that depends on the layered Request call with Response return pattern.

  • _nodeResponse - This is the underlying Node.js http.ServerResponse object. This can be used to access the raw response data, such as the raw headers. Again, this is discouraged and can cause problems for middleware, should only be used if you are certain that other server handlers will not attempt to return a different Response object.

Response

REST methods can directly return data that is serialized and returned to users, or it can return a Response object (or a promise to a Response), or it can return a Response-like object with the following properties (or again, a promise to it):

  • status - This is the HTTP status code of the response. This is a number like 200, 404, 500, etc.

  • headers - This is a Headers object that contains the headers of the response.

  • data - This is the data to be returned of the response. This will be serialized using Harper's content negotiation.

  • body - Alternately (to data), the raw body can be returned as a Buffer, string, stream (Node.js or ReadableStream), or a Blob.

HttpOptions

Type: Object

Properties:

  • runFirst - optional - boolean - Add listener to the front of the middleware chain. Defaults to false

  • port - optional - number - Specify which HTTP server middleware chain to add the listener to. Defaults to the Harper system default HTTP port configured by harperdb-config.yaml, generally 9926

  • securePort - optional - number - Specify which HTTPS server middleware chain to add the listener to. Defaults to the Harper system default HTTP secure port configured by harperdb-config.yaml, generally 9927

HttpServer

Node.js http.Server or https.SecureServer instance.

server.socket(listener: ConnectionListener, options: SocketOptions): SocketServer

Creates a socket server on the specified options.port or options.securePort.

Only one socket server will be created. A securePort takes precedence.

ConnectionListener

Node.js socket server connection listener as documented in net.createServer or tls.createServer

SocketOptions

  • port - optional - number - Specify the port for the net.Server instance.

  • securePort - optional - number - Specify the port for the tls.Server instance.

SocketServer

Node.js net.Server or tls.Server instance.

server.ws(listener: WsListener, options: WsOptions): HttpServer[]

Add a listener to the WebSocket connection listener middleware chain. The WebSocket server is associated with the HTTP server specified by the options.port or options.securePort. Use the server.upgrade() method to add a listener to the upgrade middleware chain.

Example:

server.ws((ws, request, chainCompletion) => {
	chainCompletion.then(() => {
		ws.on('error', console.error);

		ws.on('message', function message(data) {
			console.log('received: %s', data);
		});

		ws.send('something');
	});
});

WsListener

Type: (ws: WebSocket, request: Request, chainCompletion: ChainCompletion, next: WsListener): Promise<void>

The WebSocket connection listener.

  • The ws argument is the WebSocket instance as defined by the ws module.

  • The request argument is Harper's transformation of the IncomingMessage argument of the standard 'connection' listener event for a WebSocket server.

  • The chainCompletion argument is a Promise of the associated HTTP server's request chain. Awaiting this promise enables the user to ensure the HTTP request has finished being processed before operating on the WebSocket.

  • The next argument is similar to that of other next arguments in Harper's server middlewares. To continue execution of the WebSocket connection listener middleware chain, pass all of the other arguments to this one such as: next(ws, request, chainCompletion)

WsOptions

Type: Object

Properties:

  • maxPayload - optional - number - Set the max payload size for the WebSocket server. Defaults to 100 MB.

  • runFirst - optional - boolean - Add listener to the front of the middleware chain. Defaults to false

  • port - optional - number - Specify which WebSocket server middleware chain to add the listener to. Defaults to the Harper system default HTTP port configured by harperdb-config.yaml, generally 9926

  • securePort - optional - number - Specify which WebSocket secure server middleware chain to add the listener to. Defaults to the Harper system default HTTP secure port configured by harperdb-config.yaml, generally 9927

server.upgrade(listener: UpgradeListener, options: UpgradeOptions): void

Add a listener to the HTTP Server upgrade event. If a WebSocket connection listener is added using server.ws(), a default upgrade handler will be added as well. The default upgrade handler will add a __harperdb_request_upgraded boolean to the request argument to signal the connection has already been upgraded. It will also check for this boolean before upgrading and if it is true, it will pass the arguments along to the next listener.

This method should be used to delegate HTTP upgrade events to an external WebSocket server instance.

Example:

This example is from the Harper Next.js component. See the complete source code here

server.upgrade(
	(request, socket, head, next) => {
		if (request.url === '/_next/webpack-hmr') {
			return upgradeHandler(request, socket, head).then(() => {
				request.__harperdb_request_upgraded = true;

				next(request, socket, head);
			});
		}

		return next(request, socket, head);
	},
	{ runFirst: true }
);

UpgradeListener

Type: (request, socket, head, next) => void

The arguments are passed to the middleware chain from the HTTP server 'upgrade' event.

UpgradeOptions

Type: Object

Properties:

  • runFirst - optional - boolean - Add listener to the front of the middleware chain. Defaults to false

  • port - optional - number - Specify which HTTP server middleware chain to add the listener to. Defaults to the Harper system default HTTP port configured by harperdb-config.yaml, generally 9926

  • securePort - optional - number - Specify which HTTP secure server middleware chain to add the listener to. Defaults to the Harper system default HTTP secure port configured by harperdb-config.yaml, generally 9927

server.config

This provides access to the Harper configuration object. This comes from the harperdb-config.yaml (parsed into object form).

server.recordAnalytics(value, metric, path?, method?, type?)

This records the provided value as a metric into Harper's analytics. Harper efficiently records and tracks these metrics and makes them available through analytics API. The values are aggregated and statistical information is computed when many operations are performed. The optional parameters can be used to group statistics. For the parameters, make sure you are not grouping on too fine of a level for useful aggregation. The parameters are:

  • value - This is a numeric value for the metric that is being recorded. This can be a value measuring time or bytes, for example.

  • metric - This is the name of the metric.

  • path - This is an optional path (like a URL path). For a URL like /my-resource/, you would typically include a path of "my-resource", not including the id so you can group by all the requests to "my-resource" instead of individually aggregating by each individual id.

  • method - Optional method to group by.

  • type - Optional type to group by.

server.getUser(username): Promise<User>

This returns the user object with permissions/authorization information based on the provided username. This does not verify the password, so it is generally used for looking up users by username. If you want to verify a user by password, use server.authenticateUser.

server.authenticateUser(username, password): Promise<User>

This returns the user object with permissions/authorization information based on the provided username. The password will be verified before returning the user object (if the password is incorrect, an error will be thrown).

Last updated