Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions apps/docs/content/4.frameworks/00.overview.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ evlog provides native integrations for every major TypeScript framework. The sam
| [Standalone](/frameworks/standalone) | `evlog` | Manual | `createLogger()` / `createRequestLogger()` | Stable |
| [Astro](/frameworks/astro) | `evlog` | Manual | `createRequestLogger()` | Guide |
| [Custom](/frameworks/custom-integration) | `evlog/toolkit` | Build your own | `createMiddlewareLogger()` | Beta |
| [tRPC](/frameworks/trpc) | `evlog/trpc` | Middleware | `ctx.log` / `useLogger()` | Stable |
| [oRPC](/frameworks/orpc) | `evlog/orpc` | Interceptor | `context.log` / `useLogger()` | Stable |

## Full-Stack Frameworks

Expand Down
66 changes: 66 additions & 0 deletions apps/docs/content/4.frameworks/17.trpc.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
# tRPC

Wide events and structured errors in tRPC procedures.

evlog integrates with tRPC via `createEvlogMiddleware()` and `createEvlogTRPCContext()`.
Requires an evlog HTTP adapter (`evlog/next`, `evlog/express`, `evlog/hono`, etc.) mounted
on the underlying framework.

## Quick Start

### 1. Install

```
bun add evlog @trpc/server
```

### 2. Set up the HTTP adapter

Follow the setup guide for your framework (`evlog/next`, `evlog/express`, `evlog/hono`, etc.)
before adding the tRPC adapter.

### 3. Add the middleware to your base procedure

```ts
import { initTRPC } from '@trpc/server'
import { createEvlogMiddleware, createEvlogTRPCContext } from 'evlog/trpc'

const createContext = createEvlogTRPCContext(
async ({ req }) => ({ user: await getUser(req) })
)

const t = initTRPC.context<typeof createContext>().create()

export const loggedProcedure = t.procedure.use(createEvlogMiddleware())
```

### 4. Use in your procedures

```ts
export const userRouter = t.router({
getById: loggedProcedure.input(z.string()).query(({ ctx, input }) => {
ctx.log.set({ userId: input })
return getUserById(input)
}),
})
```

## Wide event fields

The tRPC adapter enriches every wide event with:

| Field | Type | Description |
|---|---|---|
| `procedure` | `string` | Full procedure path, e.g. `user.getById` |
| `type` | `string` | `query` or `mutation` |

## Known limitations

- **Always-200**: tRPC returns HTTP 200 even on errors. The adapter detects errors from the
procedure result object directly, not from the HTTP status.
- **Batching**: when tRPC request batching is enabled, the wide event reflects all procedures
in the batch but `procedure` will contain the last one. Batching is uncommon in production
and this may be improved in a future release.
- **Standalone**: running tRPC without an underlying HTTP framework (raw fetch adapter) is
not supported. An evlog HTTP adapter must be present.
- **Subscriptions**: not supported in this release.
65 changes: 65 additions & 0 deletions apps/docs/content/4.frameworks/18.orpc.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
# oRPC

Wide events and structured errors in oRPC procedures.

evlog integrates with oRPC via `createEvlogInterceptor()` and `createEvlogContext()`.
Requires an evlog HTTP adapter (`evlog/next`, `evlog/hono`, etc.) mounted on the
underlying framework.

## Quick Start

### 1. Install

```
bun add evlog @orpc/server
```

### 2. Set up the HTTP adapter

Follow the setup guide for your framework before adding the oRPC adapter.

### 3. Add the interceptor to your RPCHandler

```ts
import { RPCHandler } from '@orpc/server/fetch'
import { createEvlogInterceptor, createEvlogContext } from 'evlog/orpc'

const handler = new RPCHandler(appRouter, {
interceptors: [createEvlogInterceptor()],
})

async function handleRequest(request: Request) {
const { response } = await handler.handle(request, {
prefix: '/rpc',
context: createEvlogContext(request, {
user: await getUser(request),
}),
})
return response ?? new Response('Not found', { status: 404 })
}
```

### 4. Use the logger inside procedures

```ts
export const todoRouter = {
create: base
.input(z.object({ text: z.string() }))
.handler(async ({ input, context }) => {
context.log.set({ action: 'todo.create', text: input.text })
return createTodo(input)
}),
}
```

## Wide event fields

| Field | Type | Description |
|---|---|---|
| `procedure` | `string` | oRPC procedure path, e.g. `todo.create` |
| `input` | `unknown` | Raw procedure input |

## Known limitations

- **Standalone**: running oRPC without an underlying HTTP framework is not supported.
An evlog HTTP adapter must be present.
86 changes: 86 additions & 0 deletions bun.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

26 changes: 26 additions & 0 deletions examples/nextjs-trpc/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# evlog Next.js tRPC example

Demonstrates evlog/next + evlog/trpc with Next.js App Router.

## Run

```
bun dev
```

## Try it

```bash
# query
curl "http://localhost:3000/api/trpc/user.getById?input=%7B%22id%22%3A%22usr_123%22%7D"

# mutation
curl -X POST http://localhost:3000/api/trpc/post.create \
-H "Content-Type: application/json" \
-d '{"json":{"title":"Hello","body":"World"}}'

# health check
curl "http://localhost:3000/api/trpc/health.check"
```

Each request emits one wide event with `procedure` and `type` included alongside any context set via `ctx.log.set()` or `useLogger()`.
14 changes: 14 additions & 0 deletions examples/nextjs-trpc/app/api/trpc/[trpc]/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { fetchRequestHandler } from '@trpc/server/adapters/fetch'
import { withEvlog } from '@/lib/evlog'
import { appRouter, createContext } from '@/lib/trpc'

const handler = withEvlog((req: Request) =>
fetchRequestHandler({
endpoint: '/api/trpc',
req,
router: appRouter,
createContext,
}),
)

export { handler as GET, handler as POST }
14 changes: 14 additions & 0 deletions examples/nextjs-trpc/app/layout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import type { Metadata } from 'next'

export const metadata: Metadata = {
title: 'evlog Next.js tRPC Example',
description: 'evlog with Next.js App Router + tRPC',
}

export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="en">
<body>{children}</body>
</html>
)
}
Loading