Skip to main content
Version: v5

Extension API

As of Harper v4.6, a new iteration of the extension system called Plugins was released. Plugins simplify the API and are recommended for new extension development. See the Plugin API reference. Both extensions and plugins are supported; extensions are not yet deprecated.

Extensions are components that provide reusable building blocks for applications. There are two key types:

  • Resource Extensions — Handle specific files or directories
  • Protocol Extensions — More advanced extensions that can return a Resource Extension; primarily used for implementing higher-level protocols and custom networking handlers

An extension is distinguished from a plain component by implementing one or more of the Resource Extension or Protocol Extension API methods.

Declaring an Extension

All extensions must define a config.yaml with an extensionModule option pointing to the extension source code (path resolves from the module root directory):

extensionModule: ./extension.js

If written in TypeScript or another compiled language, point to the built output:

extensionModule: ./dist/index.js

Resource Extension

A Resource Extension processes specific files or directories. It is comprised of four function exports:

MethodThreadTiming
handleFile()All worker threadsExecuted on every restart
handleDirectory()All worker threadsExecuted on every restart
setupFile()Main thread onlyOnce, at initial start
setupDirectory()Main thread onlyOnce, at initial start

Important: harperdb restart only restarts worker threads. Code in setupFile() and setupDirectory() runs only when Harper fully shuts down and starts again—not on deploy or restart.

handleFile() and setupFile() have identical signatures. handleDirectory() and setupDirectory() have identical signatures.

Resource Extension Configuration

Resource Extensions can be configured with files and urlPath options in config.yaml:

  • filesstring | string[] | FilesOptionObject (required) — Glob pattern(s) determining which files and directories are resolved. Harper uses fast-glob for matching.

    • sourcestring | string[] (required when object form) — Glob pattern string(s)
    • only'all' | 'files' | 'directories' (optional) — Restrict matching to a single entry type. Defaults to 'all'
    • ignorestring[] (optional) — Patterns to exclude from matches
  • urlPathstring (optional) — Base URL path prepended to resolved entries

    • Starting with ./ (e.g., './static/') prepends the component name to the URL path
    • Value of . uses the component name as the base path
    • .. is invalid and causes an error
    • Leading/trailing slashes are handled automatically (/static/, static/, and /static are equivalent)

Examples:

# Serve HTML files from web/ at the /static/ URL path
static:
files: 'web/*.html'
urlPath: 'static'

# Load all GraphQL schemas from src/schema/
graphqlSchema:
files: 'src/schema/*.graphql'

# Match files in web/, excluding web/images/
static:
files:
source: 'web/**/*'
ignore: ['web/images']

# Match only files (not directories)
myExtension:
files:
source: 'dir/**/*'
only: 'files'

Resource Extension API

At minimum, a Resource Extension must implement one of the four methods. As a standalone extension, export them directly:

// ESM
export function handleFile() {}
export function setupDirectory() {}

// CJS
function handleDirectory() {}
function setupFile() {}
module.exports = { handleDirectory, setupFile };

When returned by a Protocol Extension, define them on the returned object:

export function start() {
return {
handleFile() {},
};
}

handleFile(contents, urlPath, absolutePath, resources): void | Promise<void>

setupFile(contents, urlPath, absolutePath, resources): void | Promise<void>

Process individual files. Can be async.

Parameters:

  • contentsBuffer — File contents
  • urlPathstring — Recommended URL path for the file
  • absolutePathstring — Absolute filesystem path
  • resourcesObject — Currently loaded resources

handleDirectory(urlPath, absolutePath, resources): boolean | void | Promise<boolean | void>

setupDirectory(urlPath, absolutePath, resources): boolean | void | Promise<boolean | void>

Process directories. Can be async.

If the function returns a truthy value, the component loading sequence ends and no other entries in the directory are processed.

Parameters:

  • urlPathstring — Recommended URL path for the directory
  • absolutePathstring — Absolute filesystem path
  • resourcesObject — Currently loaded resources

Protocol Extension

A Protocol Extension is a more advanced form of Resource Extension, primarily used for implementing higher-level protocols (e.g., building and running a Next.js project) or adding custom networking handlers.

Protocol Extensions use the server global API for custom networking.

Protocol Extension Configuration

In addition to the files, urlPath, and package options, Protocol Extensions accept any additional configuration options defined under the extension name in config.yaml. These options are passed through to the options object of start() and startOnMainThread().

Many protocol extensions accept port and securePort options for configuring networking handlers.

Example using @harperdb/nextjs:

'@harperdb/nextjs':
package: '@harperdb/nextjs'
files: './'
prebuilt: true
dev: false

Protocol Extension API

A Protocol Extension defines up to two methods:

MethodThreadTiming
start()All worker threadsExecuted on every restart
startOnMainThread()Main thread onlyOnce, at initial start

Both methods receive the same options object and can return a Resource Extension (an object with any of the Resource Extension methods).

start(options): ResourceExtension | Promise<ResourceExtension>

startOnMainThread(options): ResourceExtension | Promise<ResourceExtension>

Parameters:

  • optionsObject — Extension configuration options from config.yaml

Returns: An object implementing any of the Resource Extension methods

Version History

  • v4.2.0 — Extension system introduced as part of the component architecture
  • v4.6.0 — New extension API with support for dynamic reloading; Plugin API introduced as the recommended alternative