Skip to content

Commit

Permalink
feat(server)!: replace schema coercers with auto coerce plugins (#…
Browse files Browse the repository at this point in the history
…123)

* plugins can override client options

* improve client options

* ZodAutoCoercePlugin

* wip

* fix params problem

* fix
  • Loading branch information
unnoq authored Feb 6, 2025
1 parent b825e0c commit 9723092
Show file tree
Hide file tree
Showing 24 changed files with 165 additions and 317 deletions.
6 changes: 3 additions & 3 deletions apps/content/content/docs/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -152,11 +152,11 @@ import { RPCHandler } from '@orpc/server/node'
import { OpenAPIHandler } from '@orpc/openapi/node'
import { createServer } from 'node:http'
import { router } from 'examples/server'
import { ZodCoercer } from '@orpc/zod'
import { ZodAutoCoercePlugin } from '@orpc/zod'

const openAPIHandler = new OpenAPIHandler(router, {
schemaCoercers: [
new ZodCoercer(),
plugins: [
new ZodAutoCoercePlugin(),
],
})

Expand Down
36 changes: 18 additions & 18 deletions apps/content/content/docs/server/integrations.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,11 @@ Whether you're targeting serverless, edge environments, or traditional backends,
import { RPCHandler } from '@orpc/server/fetch'
import { OpenAPIHandler } from '@orpc/openapi/fetch'
import { router } from 'examples/server'
import { ZodCoercer } from '@orpc/zod'
import { ZodAutoCoercePlugin } from '@orpc/zod'

const openAPIHandler = new OpenAPIHandler(router, {
schemaCoercers: [
new ZodCoercer(),
plugins: [
new ZodAutoCoercePlugin(),
],
})

Expand Down Expand Up @@ -60,11 +60,11 @@ import { createServer } from 'node:http'
import { RPCHandler } from '@orpc/server/node'
import { OpenAPIHandler } from '@orpc/openapi/node'
import { router } from 'examples/server'
import { ZodCoercer } from '@orpc/zod'
import { ZodAutoCoercePlugin } from '@orpc/zod'

const openAPIHandler = new OpenAPIHandler(router, {
schemaCoercers: [
new ZodCoercer(),
plugins: [
new ZodAutoCoercePlugin(),
],
})
const rpcHandler = new RPCHandler(router)
Expand Down Expand Up @@ -108,13 +108,13 @@ import express from 'express'
import { RPCHandler } from '@orpc/server/node'
import { OpenAPIHandler } from '@orpc/openapi/node'
import { router } from 'examples/server'
import { ZodCoercer } from '@orpc/zod'
import { ZodAutoCoercePlugin } from '@orpc/zod'

const app = express()

const openAPIHandler = new OpenAPIHandler(router, {
schemaCoercers: [
new ZodCoercer(),
plugins: [
new ZodAutoCoercePlugin(),
],
})

Expand Down Expand Up @@ -158,14 +158,14 @@ import { Hono } from 'hono'
import { RPCHandler, createMiddleware } from '@orpc/server/hono'
import { OpenAPIHandler } from '@orpc/openapi/hono'
import { router } from 'examples/server'
import { ZodCoercer } from '@orpc/zod'
import { ZodAutoCoercePlugin } from '@orpc/zod'


const app = new Hono()

const openAPIHandler = new OpenAPIHandler(router, {
schemaCoercers: [
new ZodCoercer(),
plugins: [
new ZodAutoCoercePlugin(),
],
})

Expand Down Expand Up @@ -194,11 +194,11 @@ export default app
import { OpenAPIHandler } from '@orpc/openapi/next'
import { serve } from '@orpc/server/next'
import { router } from 'examples/server'
import { ZodCoercer } from '@orpc/zod'
import { ZodAutoCoercePlugin } from '@orpc/zod'

const openAPIHandler = new OpenAPIHandler(router, {
schemaCoercers: [
new ZodCoercer(),
plugins: [
new ZodAutoCoercePlugin(),
],
})

Expand Down Expand Up @@ -230,11 +230,11 @@ export const { GET, POST, PUT, PATCH, DELETE } = serve(rpcHandler, {
import { RPCHandler } from '@orpc/server/fetch'
import { OpenAPIHandler } from '@orpc/openapi/fetch'
import { router } from 'examples/server'
import { ZodCoercer } from '@orpc/zod'
import { ZodAutoCoercePlugin } from '@orpc/zod'

const openAPIHandler = new OpenAPIHandler(router, {
schemaCoercers: [
new ZodCoercer(),
plugins: [
new ZodAutoCoercePlugin(),
],
})

Expand Down
6 changes: 3 additions & 3 deletions apps/content/examples/contract.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import type { InferContractRouterInputs, InferContractRouterOutputs } from '@orpc/contract'
import { oc } from '@orpc/contract'
import { implement, ORPCError } from '@orpc/server'
import { oz, ZodCoercer } from '@orpc/zod'
import { oz, ZodAutoCoercePlugin } from '@orpc/zod'
import { z } from 'zod'

// Define your contract first
Expand Down Expand Up @@ -124,8 +124,8 @@ import { createServer } from 'node:http'
import { OpenAPIHandler } from '@orpc/openapi/node'

const openAPIHandler = new OpenAPIHandler(router, {
schemaCoercers: [
new ZodCoercer(),
plugins: [
new ZodAutoCoercePlugin(),
],
})

Expand Down
6 changes: 3 additions & 3 deletions apps/content/examples/server.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import type { InferRouterInputs, InferRouterOutputs } from '@orpc/server'
import { ORPCError, os } from '@orpc/server'
import { oz, ZodCoercer } from '@orpc/zod'
import { oz, ZodAutoCoercePlugin } from '@orpc/zod'
import { z } from 'zod'

export type Context = { user?: { id: string } }
Expand Down Expand Up @@ -95,8 +95,8 @@ import { createServer } from 'node:http'
import { OpenAPIHandler } from '@orpc/openapi/node'

const openAPIHandler = new OpenAPIHandler(router, {
schemaCoercers: [
new ZodCoercer(),
plugins: [
new ZodAutoCoercePlugin(),
],
})

Expand Down
1 change: 0 additions & 1 deletion packages/openapi/src/adapters/standard/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,3 @@ export * from './openapi-codec'
export * from './openapi-handler'
export * from './openapi-matcher'
export * from './openapi-serializer'
export * from './schema-coercer'
7 changes: 0 additions & 7 deletions packages/openapi/src/adapters/standard/openapi-codec.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,7 @@ describe('openAPICodec', () => {
deserialize: vi.fn(),
} as any

const coerce = vi.fn((schema, value) => value)

const codec = new OpenAPICodec({
schemaCoercers: [{ coerce }],
serializer,
})

Expand All @@ -40,8 +37,6 @@ describe('openAPICodec', () => {

expect(serializer.deserialize).toHaveBeenCalledOnce()
expect(serializer.deserialize).toHaveBeenCalledWith(url.searchParams)
expect(coerce).toHaveBeenCalledOnce()
expect(coerce).toHaveBeenCalledWith(ping['~orpc'].inputSchema, '__deserialized__')
})

it('with non-GET method', async () => {
Expand All @@ -61,8 +56,6 @@ describe('openAPICodec', () => {

expect(serializer.deserialize).toHaveBeenCalledOnce()
expect(serializer.deserialize).toHaveBeenCalledWith(serialized)
expect(coerce).toHaveBeenCalledOnce()
expect(coerce).toHaveBeenCalledWith(ping['~orpc'].inputSchema, '__deserialized__')
})
})

Expand Down
30 changes: 15 additions & 15 deletions packages/openapi/src/adapters/standard/openapi-codec.ts
Original file line number Diff line number Diff line change
@@ -1,23 +1,18 @@
import type { AnyProcedure } from '@orpc/server'
import type { StandardBody, StandardCodec, StandardHeaders, StandardParams, StandardRequest, StandardResponse } from '@orpc/server/standard'
import { fallbackContractConfig, type ORPCError } from '@orpc/contract'
import { isPlainObject, once } from '@orpc/shared'
import { isPlainObject } from '@orpc/shared'
import { OpenAPISerializer } from './openapi-serializer'
import { CompositeSchemaCoercer, type SchemaCoercer } from './schema-coercer'

export interface OpenAPICodecOptions {
serializer?: OpenAPISerializer
schemaCoercers?: SchemaCoercer[]
}

export class OpenAPICodec implements StandardCodec {
private readonly serializer: OpenAPISerializer
private readonly compositeSchemaCoercer: SchemaCoercer

constructor(options?: OpenAPICodecOptions) {
this.serializer = options?.serializer ?? new OpenAPISerializer()

this.compositeSchemaCoercer = new CompositeSchemaCoercer(options?.schemaCoercers ?? [])
}

async decode(request: StandardRequest, params: StandardParams | undefined, procedure: AnyProcedure): Promise<unknown> {
Expand All @@ -29,31 +24,36 @@ export class OpenAPICodec implements StandardCodec {
: this.serializer.deserialize(await request.body())

if (data === undefined) {
return this.compositeSchemaCoercer.coerce(procedure['~orpc'].inputSchema, params)
return params
}

if (isPlainObject(data)) {
return this.compositeSchemaCoercer.coerce(procedure['~orpc'].inputSchema, {
return {
...params,
...data,
})
}
}

return this.compositeSchemaCoercer.coerce(procedure['~orpc'].inputSchema, data)
return data
}

const query = once(() => {
const deserializeSearchParams = () => {
return this.serializer.deserialize(request.url.searchParams)
})
}

return this.compositeSchemaCoercer.coerce(procedure['~orpc'].inputSchema, {
return {
params,
get query() {
return query()
const value = deserializeSearchParams()
Object.defineProperty(this, 'query', { value, writable: true })
return value
},
set query(value) {
Object.defineProperty(this, 'query', { value, writable: true })
},
headers: request.headers,
body: this.serializer.deserialize(await request.body()),
})
}
}

encode(output: unknown, procedure: AnyProcedure): StandardResponse {
Expand Down
2 changes: 1 addition & 1 deletion packages/openapi/src/adapters/standard/openapi-matcher.ts
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ export class OpenAPIMatcher implements StandardMatcher {
return {
path: match.data.path,
procedure: match.data.procedure,
params: match.params,
params: match.params ? { ...match.params } : undefined, // normalize params to be a plain object
}
}
}
Expand Down
Loading

0 comments on commit 9723092

Please sign in to comment.