npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2025 – Pkg Stats / Ryan Hefner

@flowcore/hono-api

v0.3.0

Published

Flowcore Hono API Builder

Readme

Flowcore Hono API Builder

OpenTelemetry Trace-Log Correlation

For trace-log correlation to work properly, initialization order is critical:

✅ Correct Setup

// main.ts - FIRST THING IN APPLICATION
import init from "@flowcore/hono-api/otel"

// Initialize OTEL before any other imports
init({
  otelServiceName: "my-api",
  otelEndpoint: "http://localhost:4318", // OTLP HTTP endpoint
  runtime: "bun" // or "node" | "deno"
})

// Now safe to import and use the library
import { HonoApi, HonoApiRouter, loggerFactory } from "@flowcore/hono-api"
import { z } from "@hono/zod-openapi"

// Create logger with trace correlation
const createLogger = loggerFactory({
  prettyPrintLogs: true,
  logLevel: "info"
}).createLogger

const logger = createLogger("main")

// Your logs will now include trace_id and span_id automatically
logger.info("Application starting", { port: 3000 })

❌ Common Mistakes

// WRONG: Creating logger before OTEL initialization
import { loggerFactory } from "@flowcore/hono-api"
const logger = loggerFactory(...).createLogger("main") // No trace correlation!

import init from "@flowcore/hono-api/otel"
init({ ... }) // Too late - logger already created

Environment Variables

| Variable | Type | Description | Default | Required | |----------|------|-------------|----------|----------| | OTEL_SERVICE_NAME | string | Service name for traces | - | ✓ | | OTEL_EXPORTER_OTLP_ENDPOINT | string | OTLP endpoint URL | - | ✓ |

Global JWT Configuration

HonoApi supports global JWT configuration for custom OIDC providers like Keycloak, Okta, or Auth0. This eliminates the need to configure JWT validation per route.

✅ Global JWT Configuration

import { HonoApi, JWTValidationConfig, FLOWCORE_JWT_CONFIG } from "@flowcore/hono-api"
import { z } from "@hono/zod-openapi"

// Custom JWT configuration for Keycloak
const keycloakConfig: JWTValidationConfig = {
  extractUserId: (payload) => payload.sub as string,
  extractEmail: (payload) => payload.email as string,
  extractIsAdmin: (payload) => {
    const roles = payload.realm_access?.roles || []
    return roles.includes("admin")
  },
  validatePayload: (payload) => {
    if (!payload.sub) {
      throw new Error("Missing subject in JWT")
    }
    if (!payload.email_verified) {
      throw new Error("Email not verified")
    }
  }
}

// Option 1: Global config via auth.jwtConfig
const api = new HonoApi({
  auth: {
    jwks_url: "https://your-keycloak.com/realms/your-realm/protocol/openid-connect/certs",
    jwtConfig: keycloakConfig
  }
})

// Option 2: Global config via authDefaults (takes precedence)
const api = new HonoApi({
  auth: {
    jwks_url: "https://your-keycloak.com/realms/your-realm/protocol/openid-connect/certs",
  },
  authDefaults: {
    jwtConfig: keycloakConfig,
    optional: true // Apply to routes without explicit auth config
  }
})

JWT Configuration Interface

interface JWTValidationConfig {
  extractUserId: (payload: JWTPayload) => string
  extractEmail?: (payload: JWTPayload) => string | undefined  
  extractIsAdmin?: (payload: JWTPayload) => boolean
  validatePayload?: (payload: JWTPayload) => void
}

OIDC Provider Examples

Keycloak Configuration:

const keycloakConfig: JWTValidationConfig = {
  extractUserId: (payload) => payload.sub as string,
  extractEmail: (payload) => payload.email as string,
  extractIsAdmin: (payload) => {
    const realmRoles = payload.realm_access?.roles || []
    return realmRoles.includes("admin")
  }
}

Auth0 Configuration:

const auth0Config: JWTValidationConfig = {
  extractUserId: (payload) => payload.sub as string,
  extractEmail: (payload) => payload.email as string,
  extractIsAdmin: (payload) => {
    const roles = payload["https://myapp.com/roles"] || []
    return roles.includes("admin")
  }
}

Okta Configuration:

const oktaConfig: JWTValidationConfig = {
  extractUserId: (payload) => payload.uid as string,
  extractEmail: (payload) => payload.email as string,
  extractIsAdmin: (payload) => {
    const groups = payload.groups || []
    return groups.includes("Administrators")
  }
}

Benefits

  • No per-route configuration - JWT config applies globally
  • OIDC provider flexibility - Support any JWT issuer
  • Backward compatibility - Flowcore auth still works by default
  • Custom validation - Add business logic to JWT validation
  • Role-based access - Extract admin/user roles from JWT claims

Usage

import { HonoApi, HonoApiRouter } from "../src/mod.ts"
import { z } from "@hono/zod-openapi"

// root-router.ts
const rootRouter = new HonoApiRouter()
rootRouter.get("/health", {
  tags: ["root"],
  auth: {
    optional: true,
  },
  output: z.object({
    status: z.enum(["ok", "error"]).openapi({
      example: "ok",
    }),
  }),
  handler: () => {
    return {
      status: "ok" as const,
    }
  },
})

// data-cores-router.ts
const apiV1DataCoreRouter = new HonoApiRouter()
apiV1DataCoreRouter.post("/", {
  summary: "Create a data core",
  description: "Create a data core",
  tags: ["data-core"],
  auth: {
    permissions: (input) => {
      return [{
        action: "write",
        resource: [`frn::${input.body.tenantId}:data-core:*`],
      }]
    },
  },
  input: {
    body: z.object({
      name: z.string().openapi({
        example: "my-data-core",
      }),
      tenantId: z.string().openapi({
        example: "00000000-0000-0000-0000-000000000000",
        description: "The ID of the tenant to create the data core on",
      }),
    }),
  },
  output: z.object({
    id: z.string(),
    name: z.string(),
    tenantId: z.string(),
  }),
  handler: (input) => {
    return {
      id: crypto.randomUUID(),
      name: input.body.name,
      tenantId: input.body.tenantId,
    }
  },
})

// Hono API
const honoApi = new HonoApi({
  // Auth is optional (These are the defaults)
  auth: {
    jwks_url: "https://auth.flowcore.io/realms/flowcore/protocol/openid-connect/certs",
    api_key_url: "https://iam.api.flowcore.io",
    iam_url: "https://iam.api.flowcore.io",
  },
  // OpenAPI is optional (These are the defaults)
  openapi: {
    docPath: "/swagger",
    jsonPath: "/swagger/openapi.json",
    version: "0.0.1",
    name: "Hono API",
    description: "Hono API",
  },
  // Logger is optional (This is the default)
  logger: console,
})

honoApi.addRouter("/", rootRouter)
honoApi.addRouter("/api/v1/data-cores", apiV1DataCoreRouter)

// Serve your api in your preferred manner
Deno.serve({ port: 3000 }, honoApi.app.fetch)

Bun.serve({
  port: 3000,
  fetch: honoApi.app.fetch
})

Testing

The project includes comprehensive tests for the API functionality:

# Run all tests
deno task test

# Run tests in watch mode
deno task test:watch

# Run tests with coverage
deno task test:coverage

# Type check all files
deno task typecheck

Test Coverage

The test suite covers:

  • HonoApi Class: Initialization, configuration, router mounting, request handling
  • HonoApiRouter Class: Route registration, path handling, HTTP methods, configuration
  • Exception Classes: All error types with proper status codes and serialization
  • Request/Response Handling: GET, POST, PUT, PATCH, DELETE methods
  • Input Validation: Headers, query parameters, path parameters, request bodies
  • OpenAPI Documentation: Spec generation and documentation serving
  • Prometheus Metrics: Metrics collection and endpoint protection
  • Error Handling: Internal server errors and custom exceptions

Authentication tests are excluded as requested, but can be added separately if needed.