Katman
Integrations

Fastify

Mount Katman as a Fastify plugin — run RPC and REST routes on the same server.

Katman has a built-in serve() function that creates its own HTTP server. But if you already have a Fastify application with REST routes, middleware, and plugins, you can add Katman alongside everything else using the Fastify adapter.

When to use this

ApproachWhen
k.serve(router)New project, Katman-only server
katmanFastify(router)Adding RPC to an existing Fastify app

Setup

Install Fastify

npm install fastify

Create your Katman router

If you haven't defined a router yet, see the Getting Started guide. You need a router object — the same one you'd pass to k.serve().

Register the plugin

import  from "fastify"
import {  } from "katman/fastify"
import {  } from "./router"

const  = ()

// Your existing REST routes
.("/api/health", async () => ({ : "rest" }))

// Katman RPC under /rpc prefix
.((, {
  : () => ({
    : .user,
    : .ip,
  }),
}), { : "/rpc" })

.({ : 3000 })

Each procedure becomes a POST route under the prefix:

POST /rpc/health
POST /rpc/users/list
POST /rpc/users/create

Context from Fastify

The context option receives the Fastify request object. Use it to bridge Fastify's auth plugins, IP detection, and headers into Katman's context:

katmanFastify(appRouter, {
  : () => ({
    : .user,            // from @fastify/jwt or similar
    : .ip,                // client IP
    : .headers,      // raw headers
  }),
})

If you don't provide a context function, an empty object is used.

Content negotiation

Content negotiation works automatically. If the client sends Accept: application/x-msgpack, the Fastify adapter responds with MessagePack. Same for devalue. JSON is the default.

Fastify's built-in JSON parser handles request body parsing. The adapter only needs to encode the response.

Fastify's schema-based serialization is not used here — Katman has its own compiled serializers. Request bodies are parsed by Fastify, but response encoding is handled by Katman for consistency with serve() and handler().

Error handling

Errors from guards, wraps, and resolvers are caught and returned as JSON responses with the appropriate status code. Validation errors return 400, KatmanError returns its defined status, and unexpected errors return 500.

The error format is the same as serve() and handler():

{
  "code": "NOT_FOUND",
  "status": 404,
  "message": "Not Found"
}

What's next?

  • Serverserve() for standalone Katman servers
  • Client — connect to your Fastify-hosted Katman API
  • Protocols — JSON, MessagePack, and devalue wire formats

On this page