Skip to content

Commit 43889a7

Browse files
authored
feat(server)!: improve context (#96)
* builders * handlers * wip * fix * fix docs * fix tests * fix naming
1 parent a2e4a58 commit 43889a7

59 files changed

Lines changed: 819 additions & 772 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

apps/content/content/docs/server/lazy.mdx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ Here's how you can set up and use them:
1616
```typescript twoslash
1717
import { os, call } from '@orpc/server'
1818

19-
const pub = os.context<{ user?: { id: string } } | undefined>()
19+
const pub = os.context<{ user?: { id: string } }>()
2020

2121
// Define a router with lazy loading
2222
const router = pub.router({

apps/content/content/docs/server/server-action.mdx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ When calling `.actionable()`, you can pass a context function that provides addi
4343
import { os } from '@orpc/server'
4444
import { z } from 'zod'
4545

46-
const pub = os.context<{ db: string } | undefined>()
46+
const pub = os.context<{ db: string }>()
4747

4848
export const getting = pub
4949
.input(z.object({

apps/content/examples/server.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,13 @@ import { ORPCError, os } from '@orpc/server'
33
import { oz, ZodCoercer } from '@orpc/zod'
44
import { z } from 'zod'
55

6-
export type Context = { user?: { id: string } } | undefined
6+
export type Context = { user?: { id: string } }
77

88
// global pub, authed completely optional
99
export const pub = os.context<Context>()
1010
export const authed = pub.use(({ context, path, next }, input) => {
1111
/** put auth logic here */
12-
return next({})
12+
return next()
1313
})
1414

1515
export const router = pub.router({

packages/openapi/src/adapters/fetch/openapi-handler.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ export class OpenAPIHandler<T extends Context> implements FetchHandler<T> {
4545
}
4646

4747
async handle(request: Request, ...[options]: FetchHandleRest<T>): Promise<FetchHandleResult> {
48-
const context = options?.context as T
48+
const context = options?.context ?? {} as T
4949
const headers = request.headers
5050
const accept = headers.get('accept') || undefined
5151

packages/server/src/adapters/fetch/orpc-handler.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import type { Hooks } from '@orpc/shared'
2+
import type { Context } from '../../context'
23
import type { Router } from '../../router'
3-
import type { Context } from '../../types'
44
import type { FetchHandler, FetchHandleRest, FetchHandleResult } from './types'
55
import { ORPCError } from '@orpc/contract'
66
import { executeWithHooks, trim } from '@orpc/shared'
@@ -25,7 +25,7 @@ export class RPCHandler<T extends Context> implements FetchHandler<T> {
2525
}
2626

2727
async handle(request: Request, ...[options]: FetchHandleRest<T>): Promise<FetchHandleResult> {
28-
const context = options?.context as T
28+
const context = options?.context ?? {} as T
2929

3030
const execute = async (): Promise<FetchHandleResult> => {
3131
const url = new URL(request.url)

packages/server/src/adapters/fetch/types.test-d.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
import type { FetchHandler } from './types'
22

33
describe('FetchHandler', () => {
4-
it('optional context when context is undefinable', () => {
5-
const handler = {} as FetchHandler<{ auth: boolean } | undefined>
4+
it('optional context when all context is optional', () => {
5+
const handler = {} as FetchHandler<{ auth?: boolean }>
66

77
handler.handle(new Request('https://example.com'))
88
handler.handle(new Request('https://example.com'), { context: { auth: true } })

packages/server/src/adapters/fetch/types.ts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,14 @@
11
import type { HTTPPath } from '@orpc/contract'
2-
import type { Context } from '../../types'
2+
import type { Context } from '../../context'
33

44
export type FetchHandleOptions<T extends Context> =
55
& { prefix?: HTTPPath }
6-
& (undefined extends T ? { context?: T } : { context: T })
6+
& (Record<never, never> extends T ? { context?: T } : { context: T })
7+
8+
export type FetchHandleRest<T extends Context> =
9+
| [options: FetchHandleOptions<T>]
10+
| (Record<never, never> extends T ? [] : never)
711

8-
export type FetchHandleRest<T extends Context> = [options: FetchHandleOptions<T>] | (undefined extends T ? [] : never)
912
export type FetchHandleResult = { matched: true, response: Response } | { matched: false, response: undefined }
1013

1114
export interface FetchHandler<T extends Context> {

packages/server/src/adapters/hono/middleware.ts

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,21 @@
11
import type { Context as HonoContext, MiddlewareHandler } from 'hono'
2-
import type { Context } from '../../types'
2+
import type { Context } from '../../context'
33
import type { FetchHandleOptions, FetchHandler } from '../fetch'
44
import { value, type Value } from '@orpc/shared'
55

66
export type CreateMiddlewareOptions<T extends Context> =
77
& Omit<FetchHandleOptions<T>, 'context'>
8-
& (undefined extends T ? { context?: Value<T, [HonoContext]> } : { context: Value<T, [HonoContext]> })
8+
& (Record<never, never> extends T ? { context?: Value<T, [HonoContext]> } : { context: Value<T, [HonoContext]> })
99

10-
export type CreateMiddlewareRest<T extends Context> = [options: CreateMiddlewareOptions<T>] | (undefined extends T ? [] : never)
10+
export type CreateMiddlewareRest<T extends Context> =
11+
| [options: CreateMiddlewareOptions<T>]
12+
| (Record<never, never> extends T ? [] : never)
1113

1214
export function createMiddleware<T extends Context>(handler: FetchHandler<T>, ...[options]: CreateMiddlewareRest<T>): MiddlewareHandler {
1315
return async (c, next) => {
14-
const context = await value(options?.context, c)
16+
const context = await value(options?.context ?? {}, c) as any
1517

16-
const { matched, response } = await handler.handle(c.req.raw, { ...options, context } as any)
18+
const { matched, response } = await handler.handle(c.req.raw, { ...options, context })
1719

1820
if (matched) {
1921
c.res = response

packages/server/src/adapters/next/serve.ts

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,15 @@
11
import type { NextRequest } from 'next/server'
2-
import type { Context } from '../../types'
2+
import type { Context } from '../../context'
33
import type { FetchHandleOptions, FetchHandler } from '../fetch'
44
import { value, type Value } from '@orpc/shared'
55

66
export type ServeOptions<T extends Context> =
77
& Omit<FetchHandleOptions<T>, 'context'>
8-
& (undefined extends T ? { context?: Value<T, [NextRequest]> } : { context: Value<T, [NextRequest]> })
8+
& (Record<never, never> extends T ? { context?: Value<T, [NextRequest]> } : { context: Value<T, [NextRequest]> })
99

10-
export type ServeRest<T extends Context> = [options: ServeOptions<T>] | (undefined extends T ? [] : never)
10+
export type ServeRest<T extends Context> =
11+
| [options: ServeOptions<T>]
12+
| (Record<never, never> extends T ? [] : never)
1113

1214
export interface ServeResult {
1315
GET: (req: NextRequest) => Promise<Response>
@@ -19,7 +21,7 @@ export interface ServeResult {
1921

2022
export function serve<T extends Context>(handler: FetchHandler<T>, ...[options]: ServeRest<T>): ServeResult {
2123
const main = async (req: NextRequest) => {
22-
const context = await value(options?.context, req) as any
24+
const context = await value(options?.context ?? {}, req) as any
2325

2426
const { matched, response } = await handler.handle(req, { ...options, context })
2527

packages/server/src/adapters/node/orpc-handler.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import type { ServerResponse } from 'node:http'
2+
import type { Context } from '../../context'
23
import type { Router } from '../../router'
3-
import type { Context } from '../../types'
44
import type { RPCHandlerOptions } from '../fetch/orpc-handler'
55
import type { RequestHandler, RequestHandleRest, RequestHandleResult } from './types'
66
import { RPCHandler as ORPCFetchHandler } from '../fetch/orpc-handler'
@@ -13,18 +13,18 @@ export class RPCHandler<T extends Context> implements RequestHandler<T> {
1313
this.orpcFetchHandler = new ORPCFetchHandler(router, options)
1414
}
1515

16-
async handle(req: ExpressableIncomingMessage, res: ServerResponse, ...[options]: RequestHandleRest<T>): Promise<RequestHandleResult> {
16+
async handle(req: ExpressableIncomingMessage, res: ServerResponse, ...rest: RequestHandleRest<T>): Promise<RequestHandleResult> {
1717
const request = createRequest(req, res)
1818

19-
const castedOptions = (options ?? {}) as Exclude<typeof options, undefined>
20-
21-
const result = await this.orpcFetchHandler.handle(request, castedOptions)
19+
const result = await this.orpcFetchHandler.handle(request, ...rest)
2220

2321
if (result.matched === false) {
2422
return { matched: false }
2523
}
2624

27-
await options?.beforeSend?.(result.response, castedOptions.context as T)
25+
const context = rest[0]?.context ?? {} as T
26+
27+
await rest[0]?.beforeSend?.(result.response, context)
2828

2929
await sendResponse(res, result.response)
3030

0 commit comments

Comments
 (0)