Skip to content

Extend Body Parser

In some cases, you may need to extend the body parser to handle larger payloads or additional data types. This can be done by creating a custom body parser that extends the default functionality.

ts
import { RPCHandler } from '@orpc/server/fetch'
import { getFilenameFromContentDisposition } from '@orpc/standard-server'

const OVERRIDE_BODY_CONTEXT = Symbol('OVERRIDE_BODY_CONTEXT')

interface OverrideBodyContext {
  fetchRequest: Request
}

const handler = new RPCHandler(router, {
  adapterInterceptors: [
    (options) => {
      return options.next({
        ...options,
        context: {
          ...options.context,
          [OVERRIDE_BODY_CONTEXT as any]: {
            fetchRequest: options.request,
          },
        },
      })
    },
  ],
  rootInterceptors: [
    (options) => {
      const { fetchRequest } = (options.context as any)[OVERRIDE_BODY_CONTEXT] as OverrideBodyContext

      return options.next({
        ...options,
        request: {
          ...options.request,
          async body() {
            const contentDisposition = fetchRequest.headers.get('content-disposition')
            const contentType = fetchRequest.headers.get('content-type')

            if (contentDisposition === null && contentType?.startsWith('multipart/form-data')) {
              // Custom handling for multipart/form-data
              // Example: use @mjackson/form-data-parser for streaming parsing
              return fetchRequest.formData()
            }

            // if has content-disposition always treat as file upload
            if (
              contentDisposition !== null || (
                !contentType?.startsWith('application/json')
                && !contentType?.startsWith('application/x-www-form-urlencoded')
              )
            ) {
              // Custom handling for file uploads
              // Example: streaming file into disk to reduce memory usage
              const fileName = getFilenameFromContentDisposition(contentDisposition ?? '') ?? 'blob'
              const blob = await fetchRequest.blob()
              return new File([blob], fileName, {
                type: blob.type,
              })
            }

            // fallback to default body parser
            return options.request.body()
          },
        },
      })
    },
  ],
})

WARNING

The adapterInterceptors can be different based on the adapter you are using. The example above is for the Fetch adapter.

Released under the MIT License.