trpc-rest

Migration from trpc-to-openapi

Migrate from trpc-to-openapi to trpc-rest.

If you're using trpc-to-openapi, migrating is straightforward. The API is intentionally similar.

1. Swap the package

pnpm remove trpc-to-openapi
pnpm add trpc-rest

2. Update imports

// Before
import { createOpenApiHttpHandler } from 'trpc-to-openapi';
import { generateOpenApiDocument } from 'trpc-to-openapi';

// After
import { createOpenApiFetchHandler } from 'trpc-rest';
import { generateOpenApiDocument } from 'trpc-rest';

3. Replace the handler

The main difference: createOpenApiFetchHandler takes a Fetch Request and returns a Response. No framework-specific adapters.

// Before (Express adapter)
app.use('/api', createOpenApiHttpHandler({ router: appRouter, createContext }));

// After (any framework — just pass a Request)
const response = await createOpenApiFetchHandler({
  router: appRouter,
  endpoint: '/api',
  req: request,
  createContext,
});

Next.js App Router

app/api/[...trpc]/route.ts
import { createOpenApiFetchHandler } from 'trpc-rest';

const handler = async (req: Request) => {
  return createOpenApiFetchHandler({
    router: appRouter,
    endpoint: '/api',
    req,
    createContext: async ({ req }) => createContext(req),
  });
};

export { handler as GET, handler as POST, handler as PUT, handler as PATCH, handler as DELETE };

4. Update the generator

The generator API is the same. errorSchemas and extensions are new features you can optionally adopt.

5. Remove patches

If you had pnpm patches for trpc-to-openapi, remove them:

rm patches/trpc-to-openapi*.patch

Key differences

Behaviortrpc-to-openapitrpc-rest
Handler inputFramework-specificFetch Request
Procedure invocationInternal callingcreateCaller (full middleware)
Bodyless POST415 errorWorks correctly
Error response cachingShared by status codeNo caching issue
Optional param descriptionsStripped by unwrappingPreserved
Error formatNested data.errorFlat { message, code, issues? }

What's NOT supported

  • Express/Fastify adapters — use their Fetch compatibility layer instead
  • Content negotiation beyond JSON and form-urlencoded

On this page