@node-openapi/hapi v0.1.0
@node-openapi/hapi
Hapi adapter for
@node-openapi/coreā define Zod-validated routes, generate OpenAPI 3.1 docs, and send type-safe responses in Hapi.
Read more in the core library docs.
Table of Contents
Installation
Install the adapter along with its peer dependencies:
# npm
npm install @hapi/hapi @node-openapi/hapi
# yarn
yarn add @hapi/hapi @node-openapi/hapi
# pnpm
pnpm add @hapi/hapi @node-openapi/hapiEnsure you have a compatible version of Hapi (v21+) and Zod (v3+).
Quick Start
Create a minimal Hapi server with a GET /ping route and serve the OpenAPI JSON at /docs.
import Hapi from '@hapi/hapi';
import { OpenAPIRouter, createRoute, z } from '@node-openapi/hapi';
const init = async () => {
const server = Hapi.server({ port: 3000 });
// 1. Initialize the router
const router = new OpenAPIRouter();
// 2. Define a simple ping route
const pingRoute = createRoute({
method: 'get',
path: '/ping', // Hapi uses {param} for path parameters, same as OpenAPI
request: {},
responses: {
200: {
content: {
'application/json': { schema: z.object({ message: z.string() }) },
},
},
},
});
router.route(pingRoute, async ({ h }) => {
return h.json({ message: 'pong' });
});
// 3. Serve OpenAPI docs
router.doc('/docs', {
openapi: '3.1.0',
info: { title: 'API', version: '1.0.0' },
});
// 4. Register the router with the Hapi server
await router.registerServer(server);
// 5. Start server
await server.start();
console.log('š Server running at', server.info.uri);
console.log('š API docs available at', server.info.uri + '/docs');
};
init();API Reference
new OpenAPIRouter(options?)
options.name?: string- (default: random string) The name for the Hapi plugin created by the router.options.validateResponse?: booleanā (defaulttrue) Enable or disable runtime response validation against your OpenAPI schemas in thehhelper.
.middleware(handler)
Register a Hapi pre-handler for all routes defined on this router instance and its children.
The handler should be a Hapi RouteOptionsPreAllOptions object. You can use request.app (typed as TContext) to pass data between middlewares and to the route handler.
const router = new OpenAPIRouter<{ user?: { id: string } }>();
router.middleware({
assign: 'user', // Makes context.user available in subsequent pre-handlers and the main handler
method: async (request, h) => {
const userId = request.headers['x-user-id'];
if (typeof userId === 'string') {
request.app.user = { id: userId }; // request.app is TContext
}
return h.continue; // or simply return the value for 'user'
},
});.route(routeConfig, handler)
Define a handler for a route. The handler will run after all middlewares (pre-handlers) and request validation. The handler receives an object with:
req: the native Hapi request (Request<TRefs>)h: the Hapi response toolkit (ResponseToolkit<TRefs>), augmented withjsonandtexthelpers.input: validated request data (param, query, header, cookie, json, form).context: therequest.appobject, typed asTContext.
router.route(myRouteConfig, async ({ input, context, h, req }) => {
// ... your logic ...
// The data type is inferred by the status code.
return h.json({ data: result }); // Responds with status 200 by default
// return h.json({ error: 'Not found' }, 404);
});.use(path, subRouter)
Mount a child OpenAPIRouter under a base path. Its routes will be registered as a nested Hapi plugin. Middlewares from the parent router are passed down. OpenAPI definitions are merged.
const adminRouter = new OpenAPIRouter();
// Define routes on adminRouter...
router.use('/admin', adminRouter);.doc(path, openapiConfig)
Serve the merged OpenAPI document as JSON at path.
router.doc('/openapi.json', {
openapi: '3.1.0',
info: { title: 'My Service', version: '1.2.3' },
});.registerServer(server)
Registers this router (and its sub-routers) as a Hapi plugin with the provided Server instance.
This should be called after all routes and middlewares are defined.
const server = Hapi.server({ port: 3000 });
const router = new OpenAPIRouter();
// ... define routes, middlewares, use sub-routers ...
await router.registerServer(server);OpenAPIRouter.createRoute(routeConfig)
(also re-exported as createRoute)
Static helper to define RouteConfig objects. For Hapi, routeConfig.path should use Hapi's path parameter syntax (e.g., /articles/{slug}), which is compatible with OpenAPI.
const articleRoute = OpenAPIRouter.createRoute({
method: 'get',
path: '/articles/{slug}', // Hapi path uses {param}
request: {
params: z.object({ slug: z.string() }),
},
// ...responses
});z
Re-exported Zod instance from @node-openapi/core for defining schemas. It has additional types for OpenAPI documentation under .openapi(). See @asteasolutions/zod-to-openapi.
const userSchema = z
.object({
id: z.string(),
name: z.string(),
})
.openapi('User');Configuration
- Response validation: Disable via
new OpenAPIRouter({ validateResponse: false }). - Path parameters: Hapi uses
{paramName}for path parameters, which is the same as OpenAPI.createRoute'spathfield will be used directly.
Examples
See a complete example with authentication and CRUD routes in the examples/hapi folder:
examples/hapi/
āā package.json
āā tsconfig.json
āā src/
āā factories.ts # auth middleware factories
āā controller/
ā āā articles.controller.ts
ā āā user.controller.ts
ā āā ... (other controllers)
āā routes/
ā āā articles.routes.ts
āā index.ts # main Hapi server setupAdvanced Usage
- Context Extension: Use
.extend()on a router to create derived router instances with more specificTContexttypes (which corresponds torequest.appin Hapi). - Error handling: Zod validation errors or errors thrown in handlers will propagate. You can use Hapi's
server.ext('onPreResponse', ...)to customize error responses globally. See the exampleindex.ts. - Middleware (Pre-handler) Ordering: Middlewares are Hapi pre-handlers. They are applied in the order defined by Hapi's pre-handler system. Refer to Hapi documentation for details on ordering and assignments.
License
This project is licensed under the MIT License. See the LICENSE file for details.
11 months ago
11 months ago