Skip to content

Commit 1d3318c

Browse files
authored
feat(server): .$input (#218)
## Initial Configuration Customize the initial input schema using `.$input`: ```ts const base = os.$input(z.void()) const base = os.$input<Schema<void, unknown>>() ``` Unlike `.input`, the `.$input` method lets you redefine the input schema after its initial configuration. This is useful when you need to enforce a `void` input when no `.input` is specified. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit - **New Features** - Added enhanced input configuration capability, allowing users to flexibly redefine their initial input schema. - **Documentation** - Clarified configuration terminology by renaming "Default Configuration" to "Initial Configuration." - Introduced a new section with practical examples to guide users in customizing input schemas. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
1 parent 3b1dac3 commit 1d3318c

8 files changed

Lines changed: 95 additions & 11 deletions

File tree

apps/content/docs/openapi/input-output-structure.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -96,9 +96,9 @@ export type DetailedOutput = {
9696
9797
Make sure your handler’s return value matches this structure when using detailed mode.
9898
99-
## Default Configuration
99+
## Initial Configuration
100100
101-
Customize the default oRPC input/output structure settings using `.$route`:
101+
Customize the initial oRPC input/output structure settings using `.$route`:
102102
103103
```ts
104104
const base = os.$route({ inputStructure: 'detailed' })

apps/content/docs/openapi/routing.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -66,9 +66,9 @@ const router = {
6666
}
6767
```
6868

69-
## Default Configuration
69+
## Initial Configuration
7070

71-
Customize the default oRPC routing settings using `.$route`:
71+
Customize the initial oRPC routing settings using `.$route`:
7272

7373
```ts
7474
const base = os.$route({ method: 'GET' })

apps/content/docs/procedure.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,17 @@ const example = os
6969

7070
To learn more, see the [Middleware](/docs/middleware) documentation.
7171

72+
## Initial Configuration
73+
74+
Customize the initial input schema using `.$input`:
75+
76+
```ts
77+
const base = os.$input(z.void())
78+
const base = os.$input<Schema<void, unknown>>()
79+
```
80+
81+
Unlike `.input`, the `.$input` method lets you redefine the input schema after its initial configuration. This is useful when you need to enforce a `void` input when no `.input` is specified.
82+
7283
## Reusability
7384

7485
Each modification to a builder creates a completely new instance, avoiding reference issues. This makes it easy to reuse and extend procedures efficiently.

packages/server/src/builder-variants.test-d.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ describe('BuilderWithMiddlewares', () => {
3434
it('backward compatibility', () => {
3535
const expected = {} as OmitChainMethodDeep<
3636
typeof generalBuilder,
37-
'$config' | '$context' | '$meta' | '$route' | 'middleware'
37+
'$config' | '$context' | '$meta' | '$route' | '$input' | 'middleware'
3838
>
3939

4040
// expectTypeOf(builder).toMatchTypeOf(expected)
@@ -279,7 +279,7 @@ describe('ProcedureBuilder', () => {
279279
it('backward compatibility', () => {
280280
const expected = {} as OmitChainMethodDeep<
281281
typeof generalBuilder,
282-
'$config' | '$context' | '$meta' | '$route' | 'middleware' | 'prefix' | 'tag' | 'router' | 'lazy'
282+
'$config' | '$context' | '$meta' | '$route' | '$input' | 'middleware' | 'prefix' | 'tag' | 'router' | 'lazy'
283283
>
284284

285285
expectTypeOf(builder).toMatchTypeOf(expected)
@@ -458,7 +458,7 @@ describe('ProcedureBuilderWithInput', () => {
458458
it('backward compatibility', () => {
459459
const expected = {} as OmitChainMethodDeep<
460460
typeof generalBuilder,
461-
'$config' | '$context' | '$meta' | '$route' | 'middleware' | 'prefix' | 'tag' | 'router' | 'lazy' | 'input'
461+
'$config' | '$context' | '$meta' | '$route' | '$input' | 'middleware' | 'prefix' | 'tag' | 'router' | 'lazy' | 'input'
462462
>
463463

464464
expectTypeOf(builder).toMatchTypeOf(expected)
@@ -673,7 +673,7 @@ describe('ProcedureBuilderWithOutput', () => {
673673
it('backward compatibility', () => {
674674
const expected = {} as OmitChainMethodDeep<
675675
typeof generalBuilder,
676-
'$config' | '$context' | '$meta' | '$route' | 'middleware' | 'prefix' | 'tag' | 'router' | 'lazy' | 'output'
676+
'$config' | '$context' | '$meta' | '$route' | '$input' | 'middleware' | 'prefix' | 'tag' | 'router' | 'lazy' | 'output'
677677
>
678678

679679
// expectTypeOf(builder).toMatchTypeOf(expected)
@@ -839,7 +839,7 @@ describe('ProcedureBuilderWithInputOutput', () => {
839839
it('backward compatibility', () => {
840840
const expected = {} as OmitChainMethodDeep<
841841
typeof generalBuilder,
842-
'$config' | '$context' | '$meta' | '$route' | 'middleware' | 'prefix' | 'tag' | 'router' | 'lazy' | 'input' | 'output'
842+
'$config' | '$context' | '$meta' | '$route' | '$input' | 'middleware' | 'prefix' | 'tag' | 'router' | 'lazy' | 'input' | 'output'
843843
>
844844

845845
// expectTypeOf(builder).toMatchTypeOf(expected)
@@ -1039,7 +1039,7 @@ describe('RouterBuilder', () => {
10391039
it('backward compatibility', () => {
10401040
const expected = {} as OmitChainMethodDeep<
10411041
typeof generalBuilder,
1042-
'$config' | '$context' | '$meta' | '$route' | 'middleware' | 'meta' | 'route' | 'input' | 'output' | 'handler'
1042+
'$config' | '$context' | '$meta' | '$route' | '$input' | 'middleware' | 'meta' | 'route' | 'input' | 'output' | 'handler'
10431043
>
10441044

10451045
// expectTypeOf(builder).toMatchTypeOf(expected)

packages/server/src/builder.test-d.ts

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import type { DecoratedMiddleware } from './middleware-decorated'
1111
import type { Procedure } from './procedure'
1212
import type { DecoratedProcedure } from './procedure-decorated'
1313
import type { EnhancedRouter } from './router-utils'
14+
import { z } from 'zod'
1415
import { generalSchema } from '../../contract/tests/shared'
1516
import { router } from '../tests/shared'
1617

@@ -115,6 +116,42 @@ describe('Builder', () => {
115116
builder.$route({ method: 'INVALID' })
116117
})
117118

119+
describe('.$input', () => {
120+
it('with actual schema', () => {
121+
const schema = z.void()
122+
123+
expectTypeOf(builder.$input(schema)).toEqualTypeOf<
124+
Builder<
125+
InitialContext,
126+
CurrentContext,
127+
typeof schema,
128+
typeof outputSchema,
129+
typeof baseErrorMap,
130+
BaseMeta
131+
>
132+
>()
133+
134+
// @ts-expect-error --- invalid schema
135+
builder.$input({})
136+
})
137+
138+
it('with types only', () => {
139+
expectTypeOf(builder.$input<Schema<void, unknown>>()).toEqualTypeOf<
140+
Builder<
141+
InitialContext,
142+
CurrentContext,
143+
Schema<void, unknown>,
144+
typeof outputSchema,
145+
typeof baseErrorMap,
146+
BaseMeta
147+
>
148+
>()
149+
150+
// @ts-expect-error --- invalid schema
151+
builder.$input<'invalid'>()
152+
})
153+
})
154+
118155
describe('.middleware', () => {
119156
it('works', () => {
120157
expectTypeOf(

packages/server/src/builder.test.ts

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1+
import type { Schema } from '@orpc/contract'
12
import { isContractProcedure } from '@orpc/contract'
3+
import { z } from 'zod'
24
import { baseErrorMap, baseMeta, baseRoute, generalSchema, inputSchema, outputSchema } from '../../contract/tests/shared'
35
import { router } from '../tests/shared'
46
import { Builder } from './builder'
@@ -94,6 +96,31 @@ describe('builder', () => {
9496
})
9597
})
9698

99+
describe('.$input', () => {
100+
it('with actual schema', () => {
101+
const schema = z.void()
102+
const applied = builder.$input(schema)
103+
104+
expect(applied).instanceOf(Builder)
105+
expect(applied).not.toBe(builder)
106+
expect(applied['~orpc']).toEqual({
107+
...def,
108+
inputSchema: schema,
109+
})
110+
})
111+
112+
it('with type only', () => {
113+
const applied = builder.$input<Schema<void, unknown>>()
114+
115+
expect(applied).instanceOf(Builder)
116+
expect(applied).not.toBe(builder)
117+
expect(applied['~orpc']).toEqual({
118+
...def,
119+
inputSchema: undefined,
120+
})
121+
})
122+
})
123+
97124
it('.middleware', () => {
98125
const mid = vi.fn()
99126
const applied = builder.middleware(mid)

packages/server/src/builder.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,15 @@ export class Builder<
9898
})
9999
}
100100

101+
$input<U extends AnySchema>(
102+
initialInputSchema?: U,
103+
): Builder<TInitialContext, TCurrentContext, U, TOutputSchema, TErrorMap, TMeta> {
104+
return new Builder({
105+
...this['~orpc'],
106+
inputSchema: initialInputSchema,
107+
})
108+
}
109+
101110
middleware<UOutContext extends Context, TInput, TOutput = any>( // = any here is important to make middleware can be used in any output by default
102111
middleware: Middleware<TCurrentContext, UOutContext, TInput, TOutput, ORPCErrorConstructorMap<TErrorMap>, TMeta>,
103112
): DecoratedMiddleware<TCurrentContext, UOutContext, TInput, TOutput, ORPCErrorConstructorMap<any>, TMeta> { // ORPCErrorConstructorMap<any> ensures middleware can used in any procedure

packages/server/src/implementer-procedure.test-d.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -190,7 +190,7 @@ describe('ProcedureImplementer', () => {
190190
it('backward compatibility', () => {
191191
const expected = {} as OmitChainMethodDeep<
192192
typeof generalBuilder,
193-
'$config' | '$context' | '$meta' | '$route' | 'middleware' | 'prefix' | 'tag' | 'router' | 'lazy' | 'input' | 'output' | 'meta' | 'route' | 'errors'
193+
'$config' | '$context' | '$meta' | '$route' | '$input' | 'middleware' | 'prefix' | 'tag' | 'router' | 'lazy' | 'input' | 'output' | 'meta' | 'route' | 'errors'
194194
>
195195

196196
// expectTypeOf(builder).toMatchTypeOf(expected)

0 commit comments

Comments
 (0)