Shared packages
Integration as package
Package a custom framework integration as an npm library so your team — or the open-source community — can install evlog support for runtime X with one command.
Once you've built a working evlog integration for a new framework or runtime (Custom framework), publishing it as an npm package is the natural next step. This page walks through what your @my-org/evlog-medusa (or evlog-medusa, if it's open-source) needs to ship.
Why package this?
- Pay the integration cost once — every Medusa app benefits
- Single update path — the integration's owner ships fixes; consumers bump
- Match the built-in
evlog/<framework>ergonomics — same shape, same docs
Scaffold
src/index.ts
import { defineFrameworkIntegration } from 'evlog/toolkit'
import type { MedusaContainer, MedusaRequest, MedusaResponse } from '@medusajs/framework/types'
export interface MedusaEvlogOptions {
service?: string
// ... whatever options your integration accepts
}
const integration = defineFrameworkIntegration<MedusaRequest>({
name: 'medusa',
extractRequest: req => ({
method: req.method,
path: req.path,
headers: req.headers,
requestId: req.headers['x-request-id'] as string | undefined,
}),
attachLogger: (req, logger) => {
;(req as unknown as { evlog: typeof logger }).evlog = logger
},
})
export function evlog(options: MedusaEvlogOptions = {}) {
return integration.middleware(options)
}
Package layout
package.json
{
"name": "@my-org/evlog-medusa",
"version": "0.1.0",
"type": "module",
"main": "./dist/index.mjs",
"types": "./dist/index.d.mts",
"exports": {
".": {
"types": "./dist/index.d.mts",
"import": "./dist/index.mjs"
}
},
"files": ["dist"],
"peerDependencies": {
"evlog": "^2",
"@medusajs/framework": "^2"
}
}
tsdown.config.ts
import { defineConfig } from 'tsdown'
export default defineConfig({
entry: { 'index': 'src/index.ts' },
format: 'esm',
dts: true,
external: ['evlog', 'evlog/toolkit', '@medusajs/framework', '@medusajs/framework/types'],
})
Consuming it
// medusa-config.ts
import { evlog } from '@my-org/evlog-medusa'
export default {
// ...
apis: {
middlewares: [evlog({ service: 'shop-api' })],
},
}
The integration matches the shape of every built-in evlog/<framework> package — so it slots into existing project setups (drains, enrichers, audit, sampling) without surprise.
Publishing checklist
-
peerDependencyonevlog: ^2AND on the framework's package -
defineFrameworkIntegration(not rawcreateMiddlewareLogger) so the standard hooks fire correctly - README with a "What you get" section listing the wide event fields the integration produces (
method,path,requestId,status,duration, custom) - e2e test with the framework's test utils, asserting that a request through the middleware emits a wide event
- Document any framework-specific gotchas (request lifecycle quirks, async context propagation, etc.)
- If open-source : link to it from the Frameworks overview so the next person finds it
Examples that exist in the wild
evlog's own evlog/nuxt, evlog/next, evlog/hono, evlog/express, evlog/fastify, evlog/elysia, evlog/nestjs, evlog/sveltekit, evlog/react-router are all built on defineFrameworkIntegration. They're the canonical reference for how a packaged integration should look.