@emmett-community/emmett-expressjs-with-openapi
v0.5.0
Published
Express.js utilities for Emmett applications that want OpenAPI 3.x validation without pulling the whole Emmett core
Readme
@emmett-community/emmett-expressjs-with-openapi
Express.js utilities for Emmett applications that want OpenAPI 3.x validation without pulling the whole Emmett core. This package wires express-openapi-validator into the Emmett application builder so you can validate requests, responses, security handlers, file uploads, and formats from a single OpenAPI document.
Features
- ✅ OpenAPI 3.x validation - Validate requests, responses, security, and file uploads.
- ✅ Operation handlers - Auto-wire handlers from
operationIdorx-eov-operation-handler. - ✅ Problem Details middleware - RFC 7807 error responses out of the box.
- ✅ ETag helpers - Built-in optimistic concurrency utilities.
- ✅ Testing helpers -
ApiSpecificationandApiE2ESpecificationfor tests. - ✅ Optional add-ons - Firebase Auth security handlers and Pino HTTP logging.
Why this package?
- Built as a standalone module extracted from the original
@event-driven-io/emmettrepo so it can evolve independently. - Keeps the OpenAPI document as the single source of truth across manual routers, validation, and
operationHandlers. - Ships with batteries included: problem-details error handling, helpers for tests, and examples for real-world setups.
Installation
npm install @emmett-community/emmett-expressjs-with-openapi
npm install express-openapi-validator # optional: OpenAPI validation
npm install pino-http # optional: HTTP logging
npm install @my-f-startup/firebase-auth-express # optional: Firebase Auth handlersPeer Dependencies
Required:
@event-driven-io/emmett^0.39.1express^4.19.2express-async-errors^3.1.1http-problem-details^0.1.5
TypeScript types:
@types/express^4.17.21@types/supertest^6.0.2 (if using testing helpers)
Optional (feature-gated):
express-openapi-validator^5.3.7 (OpenAPI validation)pino-http^9.0.0 (HTTP logging)@my-f-startup/firebase-auth-express0.1.0 (Firebase Auth handlers)supertest^7.0.0 (required by testing helpers)
Versions follow what is listed in package.json.
Quick start
import {
getApplication,
createOpenApiValidatorOptions,
startAPI,
} from '@emmett-community/emmett-expressjs-with-openapi';
const app = await getApplication({
apis: [myApi],
openApiValidator: createOpenApiValidatorOptions('./openapi.yaml', {
validateRequests: true,
validateResponses: process.env.NODE_ENV === 'development',
}),
});
startAPI(app, { port: 3000 });Configuration
OpenAPI validation
const app = await getApplication({
apis: [myApi],
openApiValidator: createOpenApiValidatorOptions('./openapi.yaml', {
validateRequests: true,
validateResponses: true,
operationHandlers: './handlers',
}),
});Prefer to parse the spec once and share it? Load the .yml document manually and reuse it for routing, validation, and automatic operationHandlers.
import path from 'node:path';
import { readFileSync } from 'node:fs';
import { parse } from 'yaml';
const spec = parse(
readFileSync(new URL('./openapi.yml', import.meta.url), 'utf-8'),
);
const app = await getApplication({
apis: [usersApi],
openApiValidator: createOpenApiValidatorOptions(spec, {
operationHandlers: path.join(path.dirname(import.meta.url), './handlers'),
}),
});Highlights:
- Enable/disable request or response validation, including per-field tweaks (coercion, unknown query params, removing additional fields, etc.).
- Register custom security handlers with
validateSecurity.handlers. - Serve the parsed spec via
serveSpec, configure file upload limits, ignore health-check routes, or validate the spec itself. - Reuse
$refparsing, custom formats, and file upload middleware exactly as in the upstream validator.
Logging (optional)
Install the optional peer to enable HTTP request/response logging:
npm install pino-httpconst app = await getApplication({
pinoHttp: true, // defaults
// or:
pinoHttp: { autoLogging: false },
});See the full options in the pino-http docs.
Firebase Auth (optional)
If you use Firebase Authentication, install the optional peer and plug it into OpenAPI security handlers:
npm install @my-f-startup/firebase-auth-expressimport admin from 'firebase-admin';
import {
createFirebaseAuthSecurityHandlers,
createOpenApiValidatorOptions,
getApplication,
} from '@emmett-community/emmett-expressjs-with-openapi';
admin.initializeApp();
const app = await getApplication({
apis: [myApi],
openApiValidator: createOpenApiValidatorOptions('./openapi.yaml', {
validateSecurity: {
handlers: createFirebaseAuthSecurityHandlers(),
},
}),
});@my-f-startup/firebase-auth-express relies on firebase-admin. Make sure the Admin SDK is installed, initialized, and configured for your environment (credentials or emulator).
Observability
This package supports optional logging and tracing.
Logging
Inject a Pino-compatible logger:
import pino from 'pino';
const logger = pino();
const app = await getApplication({
observability: { logger },
});
startAPI(app, { port: 3000, logger });No logs are emitted when logger is not provided.
Logger Contract
This package defines the canonical Logger interface for the Emmett ecosystem. Implementations (Pino, Winston, etc.) MUST adapt to this contract.
interface Logger {
debug(context: Record<string, unknown>, message?: string): void;
info(context: Record<string, unknown>, message?: string): void;
warn(context: Record<string, unknown>, message?: string): void;
error(context: Record<string, unknown>, message?: string): void;
}Semantic Rules:
context(first parameter): Structured data for the log entrymessage(second parameter): Human-readable message- The order is never inverted
Pino implements this contract natively. For other loggers like Winston, you need to create an adapter.
Note: The adapter example below is for demonstration purposes only. It is NOT part of the public API and NOT an official recommendation. Users must implement their own adapters for non-Pino loggers.
import winston from 'winston';
import type { Logger } from '@emmett-community/emmett-expressjs-with-openapi';
const winstonLogger = winston.createLogger({ /* config */ });
const logger: Logger = {
debug(context, message) {
winstonLogger.debug(message ?? '', context);
},
info(context, message) {
winstonLogger.info(message ?? '', context);
},
warn(context, message) {
winstonLogger.warn(message ?? '', context);
},
error(context, message) {
winstonLogger.error(message ?? '', context);
},
};Tracing
If your application initializes OpenTelemetry, this package will emit spans automatically. Tracing is passive - spans are no-ops when OpenTelemetry is not configured.
import { tracedOn, OK } from '@emmett-community/emmett-expressjs-with-openapi';
router.post('/carts', tracedOn(async (req) => {
// Your handler logic
return OK({ body: { success: true } });
}));This package never initializes OpenTelemetry - configure your tracer provider in your application.
API Reference
Application
getApplication(options)- Creates and configures the Express app.startAPI(app, options)- Starts the HTTP server.
OpenAPI
createOpenApiValidatorOptions(apiSpec, options)- Helper to assemble OpenAPI validator config.isOpenApiValidatorAvailable()- Type guard for optional validator dependency.createFirebaseAuthSecurityHandlers(options)- Firebase Auth security handlers.registerHandlerModule(handlersPath, module)- Manual registration for operation handlers.
HTTP helpers
send,sendCreated,sendAccepted,sendProblem- Standard HTTP responses.
ETag helpers
toWeakETag,getETagFromIfMatch,getETagFromIfNotMatch,getETagValueFromIfMatch,setETag.
Testing helpers
ApiSpecification,ApiE2ESpecificationexpect,expectNewEvents,expectResponse,expectError
See docs/openapi-validation.md for the full matrix of options and extended examples.
Testing
The package includes comprehensive test coverage:
- Unit tests (
test/unit/) - Utilities for ETag handling, error mapping - Integration tests (
test/integration/) - Basic routing, OpenAPI validation, operation handlers, optimistic concurrency - E2E tests (
test/e2e/) - End-to-end workflows for all features
Running tests
# Unit tests
npm run test:unit
# Integration tests
npm run test:int
# E2E tests
npm run test:e2e
# All tests
npm testExamples
Working examples live under examples/:
examples/shopping-cart– feature-complete Emmett sample (business logic, memory store/publisher, security handlers, OpenAPI file, unit/int/e2e tests,.httpscripts).- Legacy quick starts:
examples/basic– manual routes + validation (minimal scaffolding).examples/with-security– standalone security handler demo.examples/operation-handlers– barebonesoperationHandlersshowcase.examples/firebase-auth– Firebase Auth security handlers with operation handlers.
Documentation
- Guide:
docs/openapi-validation.md– authoritative reference for configuration, advanced usage, and troubleshooting. - Emmett docs: https://event-driven-io.github.io/emmett/
Compatibility
- Node.js: tested in CI with 24.x
- Emmett: ^0.39.1
- Express: ^4.19.2
Contributing
Contributions are welcome! Please:
- Fork the repository
- Create a feature branch
- Add tests for new functionality
- Ensure all tests pass
- Submit a pull request
Development
# Install dependencies
npm install
# Build
npm run build
# Type-check
npm run build:ts
# Run tests
npm test
# Run unit tests only
npm run test:unit
# Run integration tests
npm run test:int
# Run E2E tests
npm run test:e2eLicense
MIT
Related Packages
- @event-driven-io/emmett - Core Emmett framework
- @emmett-community/emmett-google-firestore - Firestore event store
- @emmett-community/emmett-google-realtime-db - Realtime Database inline projections
- @emmett-community/emmett-google-pubsub - Pub/Sub message bus
- @event-driven-io/emmett-mongodb - MongoDB event store
Support
Made with ❤️ by the Emmett Community
