@trpc/server 11.0.0-rc.340 → 11.0.0-rc.345

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (145) hide show
  1. package/dist/@trpc/server/http.d.ts +0 -1
  2. package/dist/@trpc/server/http.d.ts.map +1 -1
  3. package/dist/@trpc/server/index.d.ts +2 -1
  4. package/dist/@trpc/server/index.d.ts.map +1 -1
  5. package/dist/adapters/aws-lambda/content-type/json/index.d.ts +10 -0
  6. package/dist/adapters/aws-lambda/content-type/json/index.d.ts.map +1 -0
  7. package/dist/adapters/aws-lambda/content-type/json/index.js +59 -0
  8. package/dist/adapters/aws-lambda/content-type/json/index.mjs +57 -0
  9. package/dist/adapters/aws-lambda/index.d.ts.map +1 -1
  10. package/dist/adapters/aws-lambda/index.js +19 -9
  11. package/dist/adapters/aws-lambda/index.mjs +19 -9
  12. package/dist/adapters/aws-lambda/utils.d.ts +3 -12
  13. package/dist/adapters/aws-lambda/utils.d.ts.map +1 -1
  14. package/dist/adapters/aws-lambda/utils.js +12 -1
  15. package/dist/adapters/aws-lambda/utils.mjs +12 -2
  16. package/dist/adapters/content-handlers/selectContentHandlerOrUnsupportedMediaType.d.ts +4 -0
  17. package/dist/adapters/content-handlers/selectContentHandlerOrUnsupportedMediaType.d.ts.map +1 -0
  18. package/dist/adapters/content-handlers/selectContentHandlerOrUnsupportedMediaType.js +22 -0
  19. package/dist/adapters/content-handlers/selectContentHandlerOrUnsupportedMediaType.mjs +20 -0
  20. package/dist/adapters/express.d.ts.map +1 -1
  21. package/dist/adapters/express.js +0 -1
  22. package/dist/adapters/express.mjs +0 -1
  23. package/dist/adapters/fastify/content-type/json/index.d.ts +8 -0
  24. package/dist/adapters/fastify/content-type/json/index.d.ts.map +1 -0
  25. package/dist/adapters/fastify/content-type/json/index.js +59 -0
  26. package/dist/adapters/fastify/content-type/json/index.mjs +57 -0
  27. package/dist/adapters/fastify/fastifyRequestHandler.d.ts +1 -9
  28. package/dist/adapters/fastify/fastifyRequestHandler.d.ts.map +1 -1
  29. package/dist/adapters/fastify/fastifyRequestHandler.js +10 -2
  30. package/dist/adapters/fastify/fastifyRequestHandler.mjs +10 -2
  31. package/dist/adapters/fastify/fastifyTRPCPlugin.d.ts +1 -1
  32. package/dist/adapters/fastify/fastifyTRPCPlugin.d.ts.map +1 -1
  33. package/dist/adapters/fastify/types.d.ts +11 -0
  34. package/dist/adapters/fastify/types.d.ts.map +1 -0
  35. package/dist/adapters/fetch/content-type/json/index.d.ts +9 -0
  36. package/dist/adapters/fetch/content-type/json/index.d.ts.map +1 -0
  37. package/dist/adapters/fetch/content-type/json/index.js +58 -0
  38. package/dist/adapters/fetch/content-type/json/index.mjs +56 -0
  39. package/dist/adapters/fetch/fetchRequestHandler.d.ts +1 -5
  40. package/dist/adapters/fetch/fetchRequestHandler.d.ts.map +1 -1
  41. package/dist/adapters/fetch/fetchRequestHandler.js +16 -2
  42. package/dist/adapters/fetch/fetchRequestHandler.mjs +16 -2
  43. package/dist/adapters/fetch/types.d.ts +9 -12
  44. package/dist/adapters/fetch/types.d.ts.map +1 -1
  45. package/dist/adapters/next-app-dir/nextAppDirCaller.d.ts +2 -7
  46. package/dist/adapters/next-app-dir/nextAppDirCaller.d.ts.map +1 -1
  47. package/dist/adapters/next.d.ts.map +1 -1
  48. package/dist/adapters/next.js +0 -1
  49. package/dist/adapters/next.mjs +0 -1
  50. package/dist/adapters/node-http/content-type/form-data/fileUploadHandler.js +0 -72
  51. package/dist/adapters/node-http/content-type/form-data/fileUploadHandler.mjs +5 -76
  52. package/dist/adapters/node-http/content-type/form-data/index.d.ts +5 -19
  53. package/dist/adapters/node-http/content-type/form-data/index.d.ts.map +1 -1
  54. package/dist/adapters/node-http/content-type/form-data/index.js +14 -39
  55. package/dist/adapters/node-http/content-type/form-data/index.mjs +15 -35
  56. package/dist/adapters/node-http/content-type/form-data/streamSlice.d.ts +2 -2
  57. package/dist/adapters/node-http/content-type/form-data/streamSlice.d.ts.map +1 -1
  58. package/dist/adapters/node-http/content-type/form-data/uploadHandler.js +0 -12
  59. package/dist/adapters/node-http/content-type/form-data/uploadHandler.mjs +1 -12
  60. package/dist/adapters/node-http/content-type/json/getPostBody.d.ts.map +1 -1
  61. package/dist/adapters/node-http/content-type/json/getPostBody.js +4 -12
  62. package/dist/adapters/node-http/content-type/json/getPostBody.mjs +4 -12
  63. package/dist/adapters/node-http/content-type/json/index.d.ts +4 -1
  64. package/dist/adapters/node-http/content-type/json/index.d.ts.map +1 -1
  65. package/dist/adapters/node-http/content-type/json/index.js +59 -10
  66. package/dist/adapters/node-http/content-type/json/index.mjs +59 -10
  67. package/dist/adapters/node-http/content-type/octet/index.d.ts +5 -0
  68. package/dist/adapters/node-http/content-type/octet/index.d.ts.map +1 -0
  69. package/dist/adapters/node-http/content-type/octet/index.js +19 -0
  70. package/dist/adapters/node-http/content-type/octet/index.mjs +17 -0
  71. package/dist/adapters/node-http/content-type/types.d.ts +8 -0
  72. package/dist/adapters/node-http/content-type/types.d.ts.map +1 -0
  73. package/dist/adapters/node-http/nodeHTTPRequestHandler.d.ts.map +1 -1
  74. package/dist/adapters/node-http/nodeHTTPRequestHandler.js +19 -21
  75. package/dist/adapters/node-http/nodeHTTPRequestHandler.mjs +20 -22
  76. package/dist/adapters/node-http/types.d.ts +8 -19
  77. package/dist/adapters/node-http/types.d.ts.map +1 -1
  78. package/dist/adapters/standalone.d.ts.map +1 -1
  79. package/dist/adapters/standalone.js +0 -1
  80. package/dist/adapters/standalone.mjs +0 -1
  81. package/dist/adapters/ws.d.ts +2 -12
  82. package/dist/adapters/ws.d.ts.map +1 -1
  83. package/dist/bundle-analysis.json +266 -207
  84. package/dist/http.js +0 -2
  85. package/dist/http.mjs +0 -1
  86. package/dist/index.js +2 -0
  87. package/dist/index.mjs +1 -0
  88. package/dist/unstable-core-do-not-import/contentTypeParsers.d.ts +16 -0
  89. package/dist/unstable-core-do-not-import/contentTypeParsers.d.ts.map +1 -0
  90. package/dist/unstable-core-do-not-import/contentTypeParsers.js +23 -0
  91. package/dist/unstable-core-do-not-import/contentTypeParsers.mjs +21 -0
  92. package/dist/unstable-core-do-not-import/http/contentType.d.ts +6 -14
  93. package/dist/unstable-core-do-not-import/http/contentType.d.ts.map +1 -1
  94. package/dist/unstable-core-do-not-import/http/index.d.ts +0 -1
  95. package/dist/unstable-core-do-not-import/http/index.d.ts.map +1 -1
  96. package/dist/unstable-core-do-not-import/http/resolveHTTPResponse.d.ts +5 -5
  97. package/dist/unstable-core-do-not-import/http/resolveHTTPResponse.d.ts.map +1 -1
  98. package/dist/unstable-core-do-not-import/http/resolveHTTPResponse.js +25 -22
  99. package/dist/unstable-core-do-not-import/http/resolveHTTPResponse.mjs +25 -22
  100. package/dist/unstable-core-do-not-import/http/types.d.ts +0 -2
  101. package/dist/unstable-core-do-not-import/http/types.d.ts.map +1 -1
  102. package/dist/unstable-core-do-not-import/rootConfig.d.ts +21 -0
  103. package/dist/unstable-core-do-not-import/rootConfig.d.ts.map +1 -1
  104. package/dist/unstable-core-do-not-import.js +0 -2
  105. package/dist/unstable-core-do-not-import.mjs +0 -1
  106. package/package.json +3 -3
  107. package/src/@trpc/server/http.ts +0 -1
  108. package/src/@trpc/server/index.ts +7 -0
  109. package/src/adapters/aws-lambda/content-type/json/index.ts +99 -0
  110. package/src/adapters/aws-lambda/index.ts +24 -9
  111. package/src/adapters/aws-lambda/utils.ts +21 -15
  112. package/src/adapters/content-handlers/selectContentHandlerOrUnsupportedMediaType.ts +21 -0
  113. package/src/adapters/express.ts +1 -6
  114. package/src/adapters/fastify/content-type/json/index.ts +97 -0
  115. package/src/adapters/fastify/fastifyRequestHandler.ts +15 -21
  116. package/src/adapters/fastify/fastifyTRPCPlugin.ts +1 -1
  117. package/src/adapters/fastify/types.ts +22 -0
  118. package/src/adapters/fetch/content-type/json/index.ts +96 -0
  119. package/src/adapters/fetch/fetchRequestHandler.ts +22 -10
  120. package/src/adapters/fetch/types.ts +22 -15
  121. package/src/adapters/next-app-dir/nextAppDirCaller.ts +2 -9
  122. package/src/adapters/next.ts +1 -6
  123. package/src/adapters/node-http/content-type/form-data/fileUploadHandler.ts +7 -7
  124. package/src/adapters/node-http/content-type/form-data/index.ts +29 -51
  125. package/src/adapters/node-http/content-type/form-data/streamSlice.ts +2 -2
  126. package/src/adapters/node-http/content-type/json/getPostBody.ts +9 -18
  127. package/src/adapters/node-http/content-type/json/index.ts +85 -5
  128. package/src/adapters/node-http/content-type/octet/index.ts +29 -0
  129. package/src/adapters/node-http/{internals/contentType.ts → content-type/types.ts} +2 -14
  130. package/src/adapters/node-http/nodeHTTPRequestHandler.ts +22 -35
  131. package/src/adapters/node-http/types.ts +46 -46
  132. package/src/adapters/standalone.ts +1 -2
  133. package/src/adapters/ws.ts +9 -14
  134. package/src/unstable-core-do-not-import/contentTypeParsers.ts +37 -0
  135. package/src/unstable-core-do-not-import/http/contentType.ts +9 -84
  136. package/src/unstable-core-do-not-import/http/index.ts +0 -1
  137. package/src/unstable-core-do-not-import/http/resolveHTTPResponse.ts +29 -28
  138. package/src/unstable-core-do-not-import/http/types.ts +0 -2
  139. package/src/unstable-core-do-not-import/rootConfig.ts +31 -0
  140. package/dist/adapters/node-http/internals/contentType.d.ts +0 -9
  141. package/dist/adapters/node-http/internals/contentType.d.ts.map +0 -1
  142. package/dist/adapters/node-http/internals/contentType.js +0 -8
  143. package/dist/adapters/node-http/internals/contentType.mjs +0 -6
  144. package/dist/unstable-core-do-not-import/http/contentType.js +0 -54
  145. package/dist/unstable-core-do-not-import/http/contentType.mjs +0 -52
@@ -0,0 +1,99 @@
1
+ // @trpc/server
2
+ import { TRPCError } from '../../../../@trpc/server';
3
+ import type {
4
+ AnyRouter,
5
+ CombinedDataTransformer,
6
+ } from '../../../../@trpc/server';
7
+ import type {
8
+ BaseContentTypeHandler,
9
+ HTTPRequest,
10
+ } from '../../../../@trpc/server/http';
11
+ import {
12
+ lambdaEventToHTTPBody,
13
+ type APIGatewayEvent,
14
+ type AWSLambdaOptions,
15
+ } from '../../utils';
16
+
17
+ export interface LambdaHTTPContentTypeHandler<
18
+ TRouter extends AnyRouter,
19
+ TEvent extends APIGatewayEvent,
20
+ > extends BaseContentTypeHandler<
21
+ AWSLambdaOptions<TRouter, TEvent> & {
22
+ event: TEvent;
23
+ req: HTTPRequest;
24
+ }
25
+ > {}
26
+
27
+ export const getLambdaHTTPJSONContentTypeHandler: <
28
+ TRouter extends AnyRouter,
29
+ TEvent extends APIGatewayEvent,
30
+ >() => LambdaHTTPContentTypeHandler<TRouter, TEvent> = () => ({
31
+ name: 'lambda-json',
32
+ isMatch(opts) {
33
+ return !!opts.event.headers['Content-Type']?.startsWith('application/json');
34
+ },
35
+ getInputs: async (opts, info) => {
36
+ function getRawProcedureInputOrThrow() {
37
+ const { event, req } = opts;
38
+
39
+ try {
40
+ if (req.method === 'GET') {
41
+ const input = req.query.get('input');
42
+ if (!input) {
43
+ return undefined;
44
+ }
45
+
46
+ return JSON.parse(input);
47
+ }
48
+
49
+ const body = lambdaEventToHTTPBody(opts.event);
50
+ if (typeof body === 'string') {
51
+ // A mutation with no inputs will have req.body === ''
52
+ return body.length === 0 ? undefined : JSON.parse(body);
53
+ }
54
+ return event.body;
55
+ } catch (cause) {
56
+ throw new TRPCError({
57
+ code: 'PARSE_ERROR',
58
+ cause,
59
+ });
60
+ }
61
+ }
62
+
63
+ const deserializeInputValue = (
64
+ rawValue: unknown,
65
+ transformer: CombinedDataTransformer,
66
+ ) => {
67
+ return typeof rawValue !== 'undefined'
68
+ ? transformer.input.deserialize(rawValue)
69
+ : rawValue;
70
+ };
71
+
72
+ const rawInput = getRawProcedureInputOrThrow();
73
+ if (rawInput === undefined) {
74
+ return undefined;
75
+ }
76
+
77
+ const transformer = opts.router._def._config.transformer;
78
+
79
+ if (!info.isBatchCall) {
80
+ return deserializeInputValue(rawInput, transformer);
81
+ }
82
+
83
+ /* istanbul ignore if */
84
+ if (
85
+ rawInput == null ||
86
+ typeof rawInput !== 'object' ||
87
+ Array.isArray(rawInput)
88
+ ) {
89
+ throw new TRPCError({
90
+ code: 'BAD_REQUEST',
91
+ message: '"input" needs to be an object when doing a batch call',
92
+ });
93
+ }
94
+
95
+ const rawValue = rawInput[info.batch];
96
+
97
+ return deserializeInputValue(rawValue, transformer);
98
+ },
99
+ });
@@ -24,6 +24,8 @@ import type {
24
24
  ResolveHTTPRequestOptionsContextFn,
25
25
  } from '../../@trpc/server/http';
26
26
  import { resolveHTTPResponse } from '../../@trpc/server/http';
27
+ import { selectContentHandlerOrUnsupportedMediaType } from '../content-handlers/selectContentHandlerOrUnsupportedMediaType';
28
+ import { getLambdaHTTPJSONContentTypeHandler } from './content-type/json';
27
29
  import type {
28
30
  APIGatewayEvent,
29
31
  APIGatewayResult,
@@ -50,18 +52,10 @@ function lambdaEventToHTTPRequest(event: APIGatewayEvent): HTTPRequest {
50
52
  }
51
53
  }
52
54
 
53
- let body: string | null | undefined;
54
- if (event.body && event.isBase64Encoded) {
55
- body = Buffer.from(event.body, 'base64').toString('utf8');
56
- } else {
57
- body = event.body;
58
- }
59
-
60
55
  return {
61
56
  method: getHTTPMethod(event),
62
57
  query: query,
63
58
  headers: event.headers,
64
- body: body,
65
59
  };
66
60
  }
67
61
 
@@ -109,18 +103,39 @@ export function awsLambdaRequestHandler<
109
103
  return async (event, context) => {
110
104
  const req = lambdaEventToHTTPRequest(event);
111
105
  const path = getPath(event);
106
+
112
107
  const createContext: ResolveHTTPRequestOptionsContextFn<TRouter> = async (
113
108
  innerOpts,
114
109
  ) => {
115
110
  return await opts.createContext?.({ event, context, ...innerOpts });
116
111
  };
117
112
 
113
+ const [contentTypeHandler, unsupportedMediaTypeError] =
114
+ selectContentHandlerOrUnsupportedMediaType(
115
+ [getLambdaHTTPJSONContentTypeHandler<TRouter, TEvent>()],
116
+ {
117
+ ...opts,
118
+ event,
119
+ req,
120
+ },
121
+ );
122
+
118
123
  const response = await resolveHTTPResponse({
119
124
  ...opts,
120
125
  createContext,
121
126
  req,
122
127
  path,
123
- error: null,
128
+ error: unsupportedMediaTypeError,
129
+ async getInput(info) {
130
+ return await contentTypeHandler?.getInputs(
131
+ {
132
+ ...opts,
133
+ event,
134
+ req,
135
+ },
136
+ info,
137
+ );
138
+ },
124
139
  onError(o) {
125
140
  opts?.onError?.({
126
141
  ...o,
@@ -14,7 +14,12 @@ import type {
14
14
  APIGatewayProxyStructuredResultV2,
15
15
  Context as APIGWContext,
16
16
  } from 'aws-lambda';
17
- import type { AnyRouter, inferRouterContext } from '../../@trpc/server'; // import @trpc/server
17
+ import type {
18
+ AnyRouter,
19
+ CreateContextCallback,
20
+ inferRouterContext,
21
+ } from '../../@trpc/server';
22
+ // import @trpc/server
18
23
 
19
24
  // @trpc/server
20
25
  import { TRPCError } from '../../@trpc/server';
@@ -50,20 +55,10 @@ export type AWSLambdaOptions<
50
55
  TEvent extends APIGatewayEvent,
51
56
  > =
52
57
  | HTTPBaseHandlerOptions<TRouter, TEvent> &
53
- (
54
- | {
55
- /**
56
- * @link https://trpc.io/docs/v11/context
57
- **/
58
- createContext: AWSLambdaCreateContextFn<TRouter, TEvent>;
59
- }
60
- | {
61
- /**
62
- * @link https://trpc.io/docs/v11/context
63
- **/
64
- createContext?: AWSLambdaCreateContextFn<TRouter, TEvent>;
65
- }
66
- );
58
+ CreateContextCallback<
59
+ inferRouterContext<AnyRouter>,
60
+ AWSLambdaCreateContextFn<TRouter, TEvent>
61
+ >;
67
62
 
68
63
  export function isPayloadV1(
69
64
  event: APIGatewayEvent,
@@ -162,3 +157,14 @@ export type APIGatewayPayloadFormatVersion =
162
157
  export const UNKNOWN_PAYLOAD_FORMAT_VERSION_ERROR_MESSAGE =
163
158
  'Custom payload format version not handled by this adapter. Please use either 1.0 or 2.0. More information here' +
164
159
  'https://docs.aws.amazon.com/apigateway/latest/developerguide/http-api-develop-integrations-lambda.html';
160
+
161
+ export function lambdaEventToHTTPBody(event: APIGatewayEvent) {
162
+ let body: string | null | undefined;
163
+ if (event.body && event.isBase64Encoded) {
164
+ body = Buffer.from(event.body, 'base64').toString('utf8');
165
+ } else {
166
+ body = event.body;
167
+ }
168
+
169
+ return body;
170
+ }
@@ -0,0 +1,21 @@
1
+ import { TRPCError } from '../../@trpc/server';
2
+ import type { BaseContentTypeHandler } from '../../@trpc/server/http';
3
+
4
+ export function selectContentHandlerOrUnsupportedMediaType<
5
+ THandlerOpts,
6
+ THandler extends BaseContentTypeHandler<THandlerOpts>,
7
+ >(handlers: THandler[], opts: THandlerOpts) {
8
+ const handler = handlers.find((handler) => handler.isMatch(opts));
9
+ if (!handler) {
10
+ return [
11
+ undefined,
12
+ new TRPCError({
13
+ code: 'UNSUPPORTED_MEDIA_TYPE',
14
+ message:
15
+ 'Invalid Content-Type header. This request may not be supported by your tRPC Adapter, or possibly by tRPC at all',
16
+ }),
17
+ ] as const;
18
+ }
19
+
20
+ return [handler] as const;
21
+ }
@@ -27,12 +27,7 @@ export function createExpressMiddleware<TRouter extends AnyRouter>(
27
27
  const endpoint = req.path.slice(1);
28
28
 
29
29
  await nodeHTTPRequestHandler({
30
- // FIXME: no typecasting should be needed here
31
- ...(opts as NodeHTTPHandlerOptions<
32
- AnyRouter,
33
- express.Request,
34
- express.Response
35
- >),
30
+ ...opts,
36
31
  req,
37
32
  res,
38
33
  path: endpoint,
@@ -0,0 +1,97 @@
1
+ // @trpc/server
2
+ import type { FastifyReply, FastifyRequest } from 'fastify';
3
+ import { TRPCError } from '../../../../@trpc/server';
4
+ import type {
5
+ AnyRouter,
6
+ CombinedDataTransformer,
7
+ } from '../../../../@trpc/server';
8
+ import type { BaseContentTypeHandler } from '../../../../@trpc/server/http';
9
+ import type { FastifyRequestHandlerOptions } from '../../types';
10
+
11
+ export interface FastifyHTTPContentTypeHandler<
12
+ TRouter extends AnyRouter,
13
+ TRequest extends FastifyRequest,
14
+ TResponse extends FastifyReply,
15
+ > extends BaseContentTypeHandler<
16
+ FastifyRequestHandlerOptions<TRouter, TRequest, TResponse>
17
+ > {}
18
+
19
+ export const getFastifyHTTPJSONContentTypeHandler: <
20
+ TRouter extends AnyRouter,
21
+ TRequest extends FastifyRequest,
22
+ TResponse extends FastifyReply,
23
+ >() => FastifyHTTPContentTypeHandler<TRouter, TRequest, TResponse> = () => ({
24
+ name: 'fastify-json',
25
+ isMatch(opts) {
26
+ return !!opts.req.headers['content-type']?.startsWith('application/json');
27
+ },
28
+ getInputs: async (opts, info) => {
29
+ async function getRawProcedureInputOrThrow() {
30
+ const { req } = opts;
31
+
32
+ try {
33
+ if (req.method === 'GET') {
34
+ const query = opts.req.query
35
+ ? new URLSearchParams(opts.req.query as any)
36
+ : new URLSearchParams(opts.req.url.split('?')[1]);
37
+
38
+ const input = query.get('input');
39
+ if (!input) {
40
+ return undefined;
41
+ }
42
+
43
+ return JSON.parse(input);
44
+ }
45
+
46
+ const body = opts.req.body ?? 'null';
47
+ if (typeof body === 'string') {
48
+ // A mutation with no inputs will have req.body === ''
49
+ return body.length === 0 ? undefined : JSON.parse(body);
50
+ }
51
+
52
+ return body;
53
+ } catch (cause) {
54
+ throw new TRPCError({
55
+ code: 'PARSE_ERROR',
56
+ cause,
57
+ });
58
+ }
59
+ }
60
+
61
+ const deserializeInputValue = (
62
+ rawValue: unknown,
63
+ transformer: CombinedDataTransformer,
64
+ ) => {
65
+ return typeof rawValue !== 'undefined'
66
+ ? transformer.input.deserialize(rawValue)
67
+ : rawValue;
68
+ };
69
+
70
+ const rawInput = await getRawProcedureInputOrThrow();
71
+ if (rawInput === undefined) {
72
+ return undefined;
73
+ }
74
+
75
+ const transformer = opts.router._def._config.transformer;
76
+
77
+ if (!info.isBatchCall) {
78
+ return deserializeInputValue(rawInput, transformer);
79
+ }
80
+
81
+ /* istanbul ignore if */
82
+ if (
83
+ rawInput == null ||
84
+ typeof rawInput !== 'object' ||
85
+ Array.isArray(rawInput)
86
+ ) {
87
+ throw new TRPCError({
88
+ code: 'BAD_REQUEST',
89
+ message: '"input" needs to be an object when doing a batch call',
90
+ });
91
+ }
92
+
93
+ const rawValue = rawInput[info.batch];
94
+
95
+ return deserializeInputValue(rawValue, transformer);
96
+ },
97
+ });
@@ -7,12 +7,11 @@
7
7
  * import type { HTTPBaseHandlerOptions } from '@trpc/server/http'
8
8
  * ```
9
9
  */
10
- import { Readable } from 'node:stream';
10
+ import { Readable } from 'stream';
11
11
  import type { FastifyReply, FastifyRequest } from 'fastify';
12
12
  // @trpc/server
13
13
  import type { AnyRouter } from '../../@trpc/server';
14
14
  import type {
15
- HTTPBaseHandlerOptions,
16
15
  HTTPRequest,
17
16
  HTTPResponse,
18
17
  ResolveHTTPRequestOptionsContextFn,
@@ -22,24 +21,9 @@ import {
22
21
  getBatchStreamFormatter,
23
22
  resolveHTTPResponse,
24
23
  } from '../../@trpc/server/http';
25
- import type { NodeHTTPCreateContextOption } from '../node-http';
26
-
27
- export type FastifyHandlerOptions<
28
- TRouter extends AnyRouter,
29
- TRequest extends FastifyRequest,
30
- TResponse extends FastifyReply,
31
- > = HTTPBaseHandlerOptions<TRouter, TRequest> &
32
- NodeHTTPCreateContextOption<TRouter, TRequest, TResponse>;
33
-
34
- type FastifyRequestHandlerOptions<
35
- TRouter extends AnyRouter,
36
- TRequest extends FastifyRequest,
37
- TResponse extends FastifyReply,
38
- > = FastifyHandlerOptions<TRouter, TRequest, TResponse> & {
39
- req: TRequest;
40
- res: TResponse;
41
- path: string;
42
- };
24
+ import { selectContentHandlerOrUnsupportedMediaType } from '../content-handlers/selectContentHandlerOrUnsupportedMediaType';
25
+ import { getFastifyHTTPJSONContentTypeHandler } from './content-type/json';
26
+ import type { FastifyRequestHandlerOptions } from './types';
43
27
 
44
28
  export async function fastifyRequestHandler<
45
29
  TRouter extends AnyRouter,
@@ -63,7 +47,6 @@ export async function fastifyRequestHandler<
63
47
  query,
64
48
  method: opts.req.method,
65
49
  headers: opts.req.headers,
66
- body: opts.req.body ?? 'null',
67
50
  };
68
51
 
69
52
  let resolve: (value: FastifyReply) => void;
@@ -108,10 +91,21 @@ export async function fastifyRequestHandler<
108
91
  }
109
92
  };
110
93
 
94
+ const [contentTypeHandler, unsupportedMediaTypeError] =
95
+ selectContentHandlerOrUnsupportedMediaType(
96
+ [getFastifyHTTPJSONContentTypeHandler<TRouter, TRequest, TResponse>()],
97
+ opts,
98
+ );
99
+
111
100
  resolveHTTPResponse({
112
101
  ...opts,
113
102
  req,
103
+ error: unsupportedMediaTypeError,
104
+ async getInput(info) {
105
+ return await contentTypeHandler?.getInputs(opts, info);
106
+ },
114
107
  createContext,
108
+
115
109
  onError(o) {
116
110
  opts?.onError?.({ ...o, req: opts.req });
117
111
  },
@@ -9,13 +9,13 @@
9
9
  */
10
10
  /// <reference types="@fastify/websocket" />
11
11
  import type { FastifyInstance, FastifyReply, FastifyRequest } from 'fastify';
12
- import type { FastifyHandlerOptions } from '.';
13
12
  // @trpc/server
14
13
  import type { AnyRouter } from '../../@trpc/server';
15
14
  import type { NodeHTTPCreateContextFnOptions } from '../node-http';
16
15
  import type { WSSHandlerOptions } from '../ws';
17
16
  import { getWSConnectionHandler } from '../ws';
18
17
  import { fastifyRequestHandler } from './fastifyRequestHandler';
18
+ import type { FastifyHandlerOptions } from './types';
19
19
 
20
20
  export interface FastifyTRPCPluginOptions<TRouter extends AnyRouter> {
21
21
  prefix?: string;
@@ -0,0 +1,22 @@
1
+ import type { FastifyReply, FastifyRequest } from 'fastify';
2
+ // @trpc/server
3
+ import type { AnyRouter } from '../../@trpc/server';
4
+ import type { HTTPBaseHandlerOptions } from '../../@trpc/server/http';
5
+ import type { NodeHTTPConditionCreateContextOption } from '../node-http';
6
+
7
+ export type FastifyHandlerOptions<
8
+ TRouter extends AnyRouter,
9
+ TRequest extends FastifyRequest,
10
+ TResponse extends FastifyReply,
11
+ > = HTTPBaseHandlerOptions<TRouter, TRequest> &
12
+ NodeHTTPConditionCreateContextOption<TRouter, TRequest, TResponse>;
13
+
14
+ export type FastifyRequestHandlerOptions<
15
+ TRouter extends AnyRouter,
16
+ TRequest extends FastifyRequest,
17
+ TResponse extends FastifyReply,
18
+ > = FastifyHandlerOptions<TRouter, TRequest, TResponse> & {
19
+ req: TRequest;
20
+ res: TResponse;
21
+ path: string;
22
+ };
@@ -0,0 +1,96 @@
1
+ // @trpc/server
2
+ import { TRPCError } from '../../../../@trpc/server';
3
+ import type {
4
+ AnyRouter,
5
+ CombinedDataTransformer,
6
+ } from '../../../../@trpc/server';
7
+ import type { BaseContentTypeHandler } from '../../../../@trpc/server/http';
8
+ import type { FetchHandlerRequestOptions } from '../../types';
9
+
10
+ export interface FetchHTTPContentTypeHandler<TRouter extends AnyRouter>
11
+ extends BaseContentTypeHandler<
12
+ FetchHandlerRequestOptions<TRouter> & {
13
+ url: URL;
14
+ }
15
+ > {}
16
+
17
+ export const getFetchHTTPJSONContentTypeHandler: <
18
+ TRouter extends AnyRouter,
19
+ >() => FetchHTTPContentTypeHandler<TRouter> = () => ({
20
+ name: 'fetch-json',
21
+ isMatch(opts) {
22
+ return !!opts.req.headers
23
+ .get('content-type')
24
+ ?.startsWith('application/json');
25
+ },
26
+ getInputs: async (opts, info) => {
27
+ async function getRawProcedureInputOrThrow() {
28
+ const { req } = opts;
29
+
30
+ try {
31
+ if (req.method === 'GET') {
32
+ const input = opts.url.searchParams.get('input');
33
+ if (!input) {
34
+ return undefined;
35
+ }
36
+
37
+ return JSON.parse(input);
38
+ }
39
+
40
+ const body = opts.req.headers
41
+ .get('content-type')
42
+ ?.startsWith('application/json')
43
+ ? await opts.req.text()
44
+ : '';
45
+
46
+ if (typeof body === 'string') {
47
+ // A mutation with no inputs will have req.body === ''
48
+ return body.length === 0 ? undefined : JSON.parse(body);
49
+ }
50
+
51
+ return body;
52
+ } catch (cause) {
53
+ throw new TRPCError({
54
+ code: 'PARSE_ERROR',
55
+ cause,
56
+ });
57
+ }
58
+ }
59
+
60
+ const deserializeInputValue = (
61
+ rawValue: unknown,
62
+ transformer: CombinedDataTransformer,
63
+ ) => {
64
+ return typeof rawValue !== 'undefined'
65
+ ? transformer.input.deserialize(rawValue)
66
+ : rawValue;
67
+ };
68
+
69
+ const rawInput = await getRawProcedureInputOrThrow();
70
+ if (rawInput === undefined) {
71
+ return undefined;
72
+ }
73
+
74
+ const transformer = opts.router._def._config.transformer;
75
+
76
+ if (!info.isBatchCall) {
77
+ return deserializeInputValue(rawInput, transformer);
78
+ }
79
+
80
+ /* istanbul ignore if */
81
+ if (
82
+ rawInput == null ||
83
+ typeof rawInput !== 'object' ||
84
+ Array.isArray(rawInput)
85
+ ) {
86
+ throw new TRPCError({
87
+ code: 'BAD_REQUEST',
88
+ message: '"input" needs to be an object when doing a batch call',
89
+ });
90
+ }
91
+
92
+ const rawValue = rawInput[info.batch];
93
+
94
+ return deserializeInputValue(rawValue, transformer);
95
+ },
96
+ });
@@ -21,14 +21,10 @@ import {
21
21
  resolveHTTPResponse,
22
22
  toURL,
23
23
  } from '../../@trpc/server/http';
24
+ import { selectContentHandlerOrUnsupportedMediaType } from '../content-handlers/selectContentHandlerOrUnsupportedMediaType';
25
+ import { getFetchHTTPJSONContentTypeHandler } from './content-type/json';
24
26
  import type { FetchHandlerOptions } from './types';
25
27
 
26
- export type FetchHandlerRequestOptions<TRouter extends AnyRouter> =
27
- FetchHandlerOptions<TRouter> & {
28
- req: Request;
29
- endpoint: string;
30
- };
31
-
32
28
  const trimSlashes = (path: string): string => {
33
29
  path = path.startsWith('/') ? path.slice(1) : path;
34
30
  path = path.endsWith('/') ? path.slice(0, -1) : path;
@@ -37,7 +33,7 @@ const trimSlashes = (path: string): string => {
37
33
  };
38
34
 
39
35
  export async function fetchRequestHandler<TRouter extends AnyRouter>(
40
- opts: FetchHandlerRequestOptions<TRouter>,
36
+ opts: FetchHandlerOptions<TRouter>,
41
37
  ): Promise<Response> {
42
38
  const resHeaders = new Headers();
43
39
 
@@ -57,9 +53,6 @@ export async function fetchRequestHandler<TRouter extends AnyRouter>(
57
53
  query: url.searchParams,
58
54
  method: opts.req.method,
59
55
  headers: Object.fromEntries(opts.req.headers),
60
- body: opts.req.headers.get('content-type')?.startsWith('application/json')
61
- ? await opts.req.text()
62
- : '',
63
56
  };
64
57
 
65
58
  let resolve: (value: Response) => void;
@@ -117,11 +110,30 @@ export async function fetchRequestHandler<TRouter extends AnyRouter>(
117
110
  }
118
111
  };
119
112
 
113
+ const [contentTypeHandler, unsupportedMediaTypeError] =
114
+ selectContentHandlerOrUnsupportedMediaType(
115
+ [getFetchHTTPJSONContentTypeHandler<TRouter>()],
116
+ {
117
+ ...opts,
118
+ url,
119
+ },
120
+ );
121
+
120
122
  resolveHTTPResponse({
121
123
  ...opts,
122
124
  req,
123
125
  createContext,
124
126
  path,
127
+ error: unsupportedMediaTypeError,
128
+ async getInput(info) {
129
+ return await contentTypeHandler?.getInputs(
130
+ {
131
+ ...opts,
132
+ url,
133
+ },
134
+ info,
135
+ );
136
+ },
125
137
  onError(o) {
126
138
  opts?.onError?.({ ...o, req: opts.req });
127
139
  },
@@ -8,7 +8,12 @@
8
8
  * ```
9
9
  */
10
10
  // @trpc/server
11
- import type { AnyRouter, inferRouterContext } from '../../@trpc/server';
11
+ import type {
12
+ AnyRouter,
13
+ CreateContextCallback,
14
+ inferRouterContext,
15
+ WrapCreateContext,
16
+ } from '../../@trpc/server';
12
17
  // @trpc/server/http
13
18
  import type {
14
19
  HTTPBaseHandlerOptions,
@@ -26,19 +31,21 @@ export type FetchCreateContextFn<TRouter extends AnyRouter> = (
26
31
  ) => inferRouterContext<TRouter> | Promise<inferRouterContext<TRouter>>;
27
32
 
28
33
  export type FetchCreateContextOption<TRouter extends AnyRouter> =
29
- unknown extends inferRouterContext<TRouter>
30
- ? {
31
- /**
32
- * @link https://trpc.io/docs/v11/context
33
- **/
34
- createContext?: FetchCreateContextFn<TRouter>;
35
- }
36
- : {
37
- /**
38
- * @link https://trpc.io/docs/v11/context
39
- **/
40
- createContext: FetchCreateContextFn<TRouter>;
41
- };
34
+ CreateContextCallback<
35
+ inferRouterContext<TRouter>,
36
+ FetchCreateContextFn<TRouter>
37
+ >;
42
38
 
43
39
  export type FetchHandlerOptions<TRouter extends AnyRouter> =
44
- FetchCreateContextOption<TRouter> & HTTPBaseHandlerOptions<TRouter, Request>;
40
+ FetchCreateContextOption<TRouter> &
41
+ HTTPBaseHandlerOptions<TRouter, Request> & {
42
+ req: Request;
43
+ endpoint: string;
44
+ };
45
+
46
+ export type FetchHandlerRequestOptions<TRouter extends AnyRouter> =
47
+ HTTPBaseHandlerOptions<TRouter, Request> &
48
+ WrapCreateContext<FetchCreateContextFn<TRouter>> & {
49
+ req: Request;
50
+ endpoint: string;
51
+ };