Skip to content
This repository has been archived by the owner on Dec 19, 2022. It is now read-only.

Commit

Permalink
Make the RPC call as property access on a Proxy obj (#21)
Browse files Browse the repository at this point in the history
  • Loading branch information
KATT authored Feb 1, 2022
1 parent dce2c79 commit 9c53240
Show file tree
Hide file tree
Showing 16 changed files with 5,810 additions and 5,876 deletions.
13 changes: 5 additions & 8 deletions .big/client.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,15 @@
import type { appRouter } from './server/routers/_app';
import { createClient } from '../src/trpc/client';
import { createRouterProxy } from '../src/trpc/client/createRouterProxy';
import type { appRouter } from './server/routers/_app';

const client = createClient<typeof appRouter>();
const { queries } = createRouterProxy<typeof appRouter>();

async function main() {
const q1 = await client.query(queries.r29q5, { hello: 'world' });
// const q1 = await client.query('r29q5', { hello: 'world' });
const result = await client.query.r0q0({ hello: 'world' });

if ('error' in q1) {
console.log(q1);
if (result.ok) {
console.log(result.data);
} else {
console.log(q1);
console.log(result.error.code);
}
}

Expand Down
6 changes: 1 addition & 5 deletions .big/server/context.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,4 @@
import {
createRouterWithContext,
initTRPC,
pipedResolver,
} from '../../src/trpc/server';
import { initTRPC } from '../../src/trpc/server';

////////////////////// app ////////////////////////////
// context
Expand Down
1 change: 1 addition & 0 deletions scripts/generate-big-f-router.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ export const __ROUTER_NAME__ = trpc.router({
__CONTENT__
}
});
`.trim();

const SERVER_DIR = __dirname + '/../.big/server/routers';
Expand Down
24 changes: 8 additions & 16 deletions src/client.ts
Original file line number Diff line number Diff line change
@@ -1,28 +1,20 @@
import type { appRouter } from './server';
import { createClient } from './trpc/client';
import { createRouterProxy } from './trpc/client/createRouterProxy';

const client = createClient<typeof appRouter>();
const { queries } = createRouterProxy<typeof appRouter>();

async function main() {
{
// you can CMD+click `greeting` below to get to the definition
const greeting = await client.query(queries.greeting, { hello: 'world' });
// you can CMD+click `post.all` below to get to the definition
const posts = await client.query(queries['post.all']);
// you can CMD+click `postAll` / `postById` here
const greeting = await client.query.postList();
const byId = await client.query.postById({ id: '1' });

console.log({ greeting, posts });
if (byId.ok) {
console.log('data', byId.data);
} else {
console.log(byId.error.code);
}

{
// we can also use string based path if we prefer that
// (but then you can't click your way to the backend)
const greeting = await client.query('greeting', { hello: 'string' });
const posts = await client.query('post.all');

console.log({ greeting, posts });
}
console.log({ greeting, byId });
}

main();
15 changes: 10 additions & 5 deletions src/server.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ import { inferProcedure, initTRPC } from './trpc/server';
async function main() {
{
// query 'whoami'
const result = await appRouter.queries['viewer.whoami']({ ctx: {} });
if (typeof result === 'object' && 'error' in result) {
const result = await appRouter.queries['viewerWhoami']();
if (result.error) {
expectTypeOf<typeof result['error']>().toMatchTypeOf<
| {
code: 'UNAUTHORIZED';
Expand All @@ -25,7 +25,7 @@ async function main() {
}
{
// if you hover result we can see that we can infer both the result and every possible expected error
const result = await appRouter.queries.greeting({ ctx: {} });
const result = await appRouter.queries.greeting({ hello: 'there' });
if ('error' in result && result.error) {
if ('zod' in result.error) {
// zod error inferred - useful for forms w/o libs
Expand Down Expand Up @@ -60,13 +60,18 @@ async function main() {
const trpc = initTRPC();
trpc.router({
queries: {
test: () => {
test: trpc.resolver(() => {
return 'ok';
},
}),
},
// @ts-expect-error should error
doesNotExist: {},
});
}
{
const result = await appRouter.mutations['fireAndForget']('hey');
console.log(result);
}
}

main();
27 changes: 21 additions & 6 deletions src/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,12 +36,12 @@ let postsDb = [
/////////// app root router //////////
export const appRouter = trpc.router({
queries: {
// simple procedure without args avialable at `post.all`
'post.all': (_params) => {
// simple procedure without args avialable at postAll`
postList: trpc.resolver((_params) => {
return postsDb;
},
}),
// get post by id or 404 if it's not found
'post.byId': trpc.resolver(
postById: trpc.resolver(
trpc.zod(
z.object({
id: z.string(),
Expand Down Expand Up @@ -76,18 +76,25 @@ export const appRouter = trpc.router({
},
),
// procedure with auth
'viewer.whoami': trpc.resolver(
viewerWhoami: trpc.resolver(
// `isAuthed()` will propagate new `ctx`
isAuthed(),
({ ctx }) => {
// `ctx.user` is now `NonNullable`
return `your id is ${ctx.user.id}`;
},
),
// a bit iffy - if you want to use it without `trpc.resolver()`
aQueryWithoutResolverWrapper: () => {
return {
ok: true,
data: 'test',
};
},
},
mutations: {
// mutation with auth + input
'post.add': trpc.resolver(
postAdd: trpc.resolver(
trpc.zod(
z.object({
title: z.string(),
Expand All @@ -107,5 +114,13 @@ export const appRouter = trpc.router({
};
},
),
// mutation without a return type
fireAndForget: trpc.resolver(
//
trpc.zod(z.string()),
() => {
// no return
},
),
},
});
65 changes: 22 additions & 43 deletions src/trpc/client/client.ts
Original file line number Diff line number Diff line change
@@ -1,48 +1,27 @@
import type { inferProcedure, Procedure, ProceduresByType } from '../server';
import type { ProceduresByType } from '../server';

type ValueOf<T> = T[keyof T];
function createRouterProxy<TRouter extends ProceduresByType<any>>() {
return new Proxy({} as any, {
get(_, type: string) {
return new Proxy({} as any, {
get(_, path: string) {
return type + '.' + path;
},
});
},
}) as any as TRouter;
}

type inferProcedureArgs<TProcedure extends Procedure<any, any>> =
undefined extends inferProcedure<TProcedure>['_input_in']
? [inferProcedure<TProcedure>['_input_in']?]
: [inferProcedure<TProcedure>['_input_in']];
export interface TRPCClient<TRouter extends ProceduresByType<any>> {
query: NonNullable<TRouter['queries']>;
mutation: NonNullable<TRouter['mutations']>;
$query: never; // Observable version of query?
}

type ExpectProcedure<T> = T extends Procedure<any, any> ? T : never;
export function createClient<TRouter extends ProceduresByType<any>>() {
function query<TPath extends keyof TRouter['queries'] & string>(
path: TPath,
...args: inferProcedureArgs<ExpectProcedure<TRouter['queries'][TPath]>>
): Promise<
inferProcedure<ExpectProcedure<TRouter['queries'][TPath]>>['result']
>;
function query<
TProcedure extends ValueOf<TRouter['queries']> & Procedure<any, any>,
>(
path: TProcedure,
...args: inferProcedureArgs<TProcedure>
): Promise<inferProcedure<TProcedure>['result']>;
function query(..._args: any[]): any {
throw new Error('Unimplemented');
}
function mutation<
TPath extends keyof TRouter['mutations'] & string,
TProcedure extends TRouter['mutations'][TPath] & Procedure<any, any>,
>(
path: TPath,
...args: inferProcedureArgs<TProcedure>
): Promise<inferProcedure<TProcedure>['result']>;
function mutation<
TProcedure extends ValueOf<TRouter['mutations']> & Procedure<any, any>,
>(
path: TProcedure,
...args: inferProcedureArgs<TProcedure>
): Promise<inferProcedure<TProcedure>['result']>;
function mutation(..._args: any[]): any {
throw new Error('Unimplemented');
}
export function createClient<
TRouter extends ProceduresByType<any>,
>(): TRPCClient<TRouter> {
const proxy = createRouterProxy<TRouter>();

return {
query,
mutation,
};
return proxy as any;
}
13 changes: 0 additions & 13 deletions src/trpc/client/createRouterProxy.ts

This file was deleted.

65 changes: 65 additions & 0 deletions src/trpc/server/middlewares/core.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import { ProcedureResultError, MaybePromise } from '../procedure';
const middlewareMarker = Symbol('middlewareMarker');

///////// middleware implementation ///////////
interface MiddlewareNextResultBase<TParams> {
/**
* If the resolver is a middleware, it should pass through their `next()`'s output.
*/
readonly _next: typeof middlewareMarker;
TParams: TParams;
}
interface MiddlewareReturnResultBase<_TParams> {
/**
* If the resolver is a middleware, it should pass through their `next()`'s output.
*/
readonly _return: typeof middlewareMarker;
}
export interface MiddlewareNextOKResult<TParams>
extends MiddlewareNextResultBase<TParams> {}
export interface MiddlewareNextErrorResult<TParams>
extends MiddlewareNextResultBase<TParams>,
ProcedureResultError<any> {}

export interface MiddlewareReturnOKResult<TParams>
extends MiddlewareReturnResultBase<TParams> {}
export interface MiddlewareReturnErrorResult<TParams>
extends MiddlewareReturnResultBase<TParams>,
ProcedureResultError<any> {}

export type MiddlewareNextResult<TParams> =
| MiddlewareNextOKResult<TParams>
| MiddlewareNextErrorResult<TParams>;

export type MiddlewareReturnResult<TParams> =
| MiddlewareReturnOKResult<TParams>
| MiddlewareReturnErrorResult<TParams>;

export type MiddlewareParams<TInputParams> = TInputParams & {
next: {
(): Promise<MiddlewareNextResult<TInputParams>>;
<T>(params: T): Promise<MiddlewareNextResult<T>>;
};
};

export type Middleware<TInputParams, TNextParams, TResult = never> = (
params: MiddlewareParams<TInputParams>,
) => MaybePromise<
MiddlewareNextResult<TNextParams> | MiddlewareReturnResult<TResult>
>;

type ExcludeMiddlewareReturnResult<T> = T extends MiddlewareReturnResult<any>
? never
: T;

export type ExcludeMiddlewareNextResult<T> = T extends MiddlewareNextResult<any>
? never
: T;

export type ExcludeMiddlewareResult<T> = ExcludeMiddlewareReturnResult<
ExcludeMiddlewareNextResult<T>
>;

export function middlewareResult<T>(_result: T): MiddlewareReturnResult<T> {
throw new Error('Unimplemented');
}
12 changes: 4 additions & 8 deletions src/trpc/server/middlewares/newContext.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,5 @@
import {
MaybePromise,
MiddlewareFunction,
Params,
ProcedureResultError,
} from '..';
import { MaybePromise, Params, ProcedureResultError } from '..';
import { Middleware, middlewareResult } from './core';

type IsProcedureResultErrorLike<T> = T extends ProcedureResultError<any>
? T
Expand All @@ -19,7 +15,7 @@ export function createNewContext<TInputContext>() {
{ ctx: TNewContext } | IsProcedureResultErrorLike<TError>
>,
) {
return function newContext<TInputParams extends {}>(): MiddlewareFunction<
return function newContext<TInputParams extends {}>(): Middleware<
TInputParams,
Omit<TInputParams, 'ctx'> & { ctx: NonNullable<TNewContext> },
TError
Expand All @@ -33,7 +29,7 @@ export function createNewContext<TInputContext>() {
ctx: result.ctx as NonNullable<TNewContext>,
});
}
return result;
return middlewareResult(result);
};
};
};
Expand Down
20 changes: 9 additions & 11 deletions src/trpc/server/middlewares/zod.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,13 @@
import type { z } from 'zod';
import {
error,
InputSchema,
MiddlewareFunction,
ProcedureResultError,
} from '..';
import { error, InputSchema, ProcedureResultError } from '..';
import { Middleware, middlewareResult } from './core';

/***
* Utility for creating a zod middleware
*/
export function zod<TInputParams, TSchema extends z.ZodTypeAny>(
schema: TSchema,
): MiddlewareFunction<
): Middleware<
TInputParams,
TInputParams & InputSchema<z.input<TSchema>, z.output<TSchema>>,
ProcedureResultError<{
Expand All @@ -36,10 +32,12 @@ export function zod<TInputParams, TSchema extends z.ZodTypeAny>(
}

const zod = (result as z.SafeParseError<zInput>).error.format();
const err = error({
code: 'BAD_REQUEST',
zod,
});
const err = middlewareResult(
error({
code: 'BAD_REQUEST',
zod,
}),
);
return err;
};
}
Loading

0 comments on commit 9c53240

Please sign in to comment.