@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.
6 months ago
6 months ago