Electrode Hapi Compatibility Utility
The `electrode-hapi-compat` utility provides functions to simplify the creation and detection of Hapi.js plugins compatible across different major versions, specifically Hapi 16 and Hapi 17/18+. It directly addresses the breaking change in Hapi plugin signatures introduced with Hapi 17, offering a `universalHapiPlugin` function. This function allows developers to define distinct plugin implementations for Hapi 16 and Hapi 17+ environments, automatically serving the correct version based on the Hapi framework detected in the host application. The package also includes helper functions such as `isHapi17OrUp` and `isHapi18OrUp` for conditional logic and offers a mechanism to manually set the Hapi version for testing. The current stable version is 1.3.3. While not on a strict regular cadence, it receives updates for broader utility, such as supporting other frameworks like Fastify in earlier minor versions. Its primary differentiator is providing a concise, focused solution for Hapi plugin compatibility across significant framework version boundaries.
Common errors
-
TypeError: server.decorate is not a function
cause Attempting to use `server.decorate` (or other Hapi API methods) in a plugin implementation that does not match the detected Hapi version (e.g., using a Hapi 17+ feature in a Hapi 16 plugin, or vice-versa).fixEnsure your `hapi16` and `hapi17OrUp` plugin implementations provided to `universalHapiPlugin` correctly match the API for their respective Hapi versions. Verify `electrode-hapi-compat` is detecting the Hapi version correctly, or explicitly set `require('electrode-hapi-compat').hapiVersion` for focused testing. -
Plugin registration failed: The plugin must be a function
cause This error typically occurs if `universalHapiPlugin` is not returning a valid Hapi plugin function, or if the `registers` object or `pkg` object passed to it are malformed or missing required properties.fixCheck that the `registers` object contains valid `hapi16` and `hapi17OrUp` functions, and that the `pkg` object has at least `name` and `version` properties as shown in the `universalHapiPlugin` examples in the documentation. Ensure `universalHapiPlugin` is called correctly and its result is used as the plugin. -
ReferenceError: require is not defined in ES module scope
cause You are attempting to use the CommonJS `require()` syntax directly within an ECMAScript Module (ESM) file, which is not supported by default without specific Node.js loader configurations or bundler setups.fixFor compatibility with this library's CommonJS-oriented examples, ensure your file is treated as CommonJS (e.g., `.js` file with `"type": "commonjs"` in `package.json`, or using a `.cjs` file extension). If you must use ESM, consider dynamic `import('electrode-hapi-compat')` to load the module asynchronously.
Warnings
- breaking Hapi.js version 17 introduced a breaking change in plugin registration signatures, moving from a `(server, options, next)` callback pattern to an `(server, options)` async/await pattern. This utility exists specifically to bridge this gap, meaning direct porting of Hapi 16 plugins to Hapi 17+ without adaptation will fail.
- gotcha Manually setting `hapiVersion` using `require('electrode-hapi-compat').hapiVersion = N` modifies a global state. This can lead to unexpected behavior in complex testing environments or applications where multiple modules might inadvertently affect or rely on this global setting, especially in concurrent test runs or if not properly reset.
- gotcha This library primarily uses and showcases CommonJS `require()` syntax in its examples. While Node.js generally supports `require()` for CJS modules in an ESM context, direct `import` statements for named exports like `universalHapiPlugin` may not work as expected without specific transpilation or configuration, given the historical context of Hapi's ecosystem.
Install
-
npm install electrode-hapi-compat -
yarn add electrode-hapi-compat -
pnpm add electrode-hapi-compat
Imports
- universalHapiPlugin
import { universalHapiPlugin } from 'electrode-hapi-compat';const { universalHapiPlugin } = require("electrode-hapi-compat"); - isHapi17OrUp
import { isHapi17OrUp } from 'electrode-hapi-compat';const { isHapi17OrUp } = require("electrode-hapi-compat"); - isHapi18OrUp
import { isHapi18OrUp } from 'electrode-hapi-compat';const { isHapi18OrUp } = require("electrode-hapi-compat"); - hapiVersion
import { hapiVersion } from 'electrode-hapi-compat';require("electrode-hapi-compat").hapiVersion;
Quickstart
const { universalHapiPlugin, isHapi17OrUp } = require("electrode-hapi-compat");
const Hapi = require('@hapi/hapi'); // Ensure Hapi is installed (e.g., npm i @hapi/hapi)
const registers = {
hapi16: (server, options, next) => {
console.log("Registering Hapi 16 plugin for:", pkg.name);
server.decorate('server', 'myHapi16Feature', 'Hapi16 Specific Value');
next();
},
hapi17OrUp: (server, options) => {
console.log("Registering Hapi 17+ plugin for:", pkg.name);
server.decorate('server', 'myHapi17Feature', 'Hapi17+ Specific Value');
}
};
const pkg = {
name: "MyUniversalCompatPlugin",
version: "1.0.0"
};
const myUniversalPlugin = universalHapiPlugin(registers, pkg);
async function startServer() {
const server = Hapi.server({ port: 3000, host: 'localhost' });
// For demonstration, manually set hapiVersion if Hapi is not installed or detected
// For example, to simulate Hapi 16:
// require("electrode-hapi-compat").hapiVersion = 16;
await server.register({
plugin: myUniversalPlugin,
options: {}
});
const detectedVersion = require("electrode-hapi-compat").hapiVersion;
console.log(`Hapi version detected by electrode-hapi-compat: ${detectedVersion}`);
if (isHapi17OrUp()) {
console.log("Server has myHapi17Feature:", server.myHapi17Feature);
} else {
console.log("Server has myHapi16Feature:", server.myHapi16Feature);
}
await server.start();
console.log('Server running on %s', server.info.uri);
}
startServer();