Skip to content

Commit

Permalink
feat(trpc): add rxjs observable compatible trpc client (analogjs#385)
Browse files Browse the repository at this point in the history
This commit introduces a rxjs version of the tRPC client. Instead of returning a Promise, the client now
returns observables for all operations. Therefore it behaves similar to the built in Angular HttpClient.

closes analogjs#379
  • Loading branch information
Dafnik authored and Villanuevand committed Sep 12, 2023
1 parent a44939c commit 9749309
Show file tree
Hide file tree
Showing 7 changed files with 471 additions and 403 deletions.
26 changes: 13 additions & 13 deletions apps/trpc-app/index.html
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
<!DOCTYPE html>
<html lang='en'>
<head>
<meta charset='utf-8' />
<title>SPARTAN - Notes App</title>
<base href='/' />
<meta name='viewport' content='width=device-width, initial-scale=1' />
<link rel="icon" type="image/x-icon" href="/favicon.ico">
<link rel='stylesheet' href='/src/styles.css' />
</head>
<body>
<trpc-app-root></trpc-app-root>
<script type='module' src='/src/main.ts'></script>
</body>
<html class="dark h-full" lang="en">
<head>
<meta charset="utf-8" />
<title>SPARTAN - Notes App</title>
<base href="/" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="icon" type="image/x-icon" href="/favicon.ico" />
<link rel="stylesheet" href="/src/styles.css" />
</head>
<body class="h-full bg-zinc-900">
<trpc-app-root></trpc-app-root>
<script type="module" src="/src/main.ts"></script>
</body>
</html>
3 changes: 3 additions & 0 deletions apps/trpc-app/src/app/app.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ import { RouterOutlet } from '@angular/router';
selector: 'trpc-app-root',
standalone: true,
imports: [RouterOutlet],
host: {
class: 'max-w-screen-md mx-auto block h-full bg-zinc-900 text-zinc-50',
},
changeDetection: ChangeDetectionStrategy.Default,
template: ` <router-outlet></router-outlet> `,
})
Expand Down
464 changes: 82 additions & 382 deletions apps/trpc-app/src/app/pages/index.page.ts

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions apps/trpc-app/src/server/trpc/context.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { inferAsyncReturnType } from '@trpc/server';

/**
* Creates context for an incoming request
* @link https://trpc.io/docs/context
Expand Down
12 changes: 4 additions & 8 deletions packages/trpc/src/lib/client/client.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,14 @@
/**
* Inspired by this awesome project to integrate trpc more into the angular way
* of doing things https://github.com/Dafnik/ngx-trpc
*/
import { InjectionToken, Provider, TransferState } from '@angular/core';
import 'isomorphic-fetch';
import { createTRPCProxyClient, httpBatchLink } from '@trpc/client';
import { httpBatchLink } from '@trpc/client';
import { AnyRouter } from '@trpc/server';
import { transferStateLink } from './links/transfer-state-link';
import {
provideTrpcCacheState,
provideTrpcCacheStateStatusManager,
tRPC_CACHE_STATE,
} from './cache-state';
import { createTRPCRxJSProxyClient } from './trpc-rxjs-proxy';
import { CreateTRPCClientOptions } from '@trpc/client/src/createTRPCUntypedClient';

export type TrpcOptions<T extends AnyRouter> = {
Expand All @@ -20,9 +17,8 @@ export type TrpcOptions<T extends AnyRouter> = {
};

export type TrpcClient<AppRouter extends AnyRouter> = ReturnType<
typeof createTRPCProxyClient<AppRouter>
typeof createTRPCRxJSProxyClient<AppRouter>
>;

const tRPC_INJECTION_TOKEN = new InjectionToken<unknown>(
'@analogjs/trpc proxy client'
);
Expand All @@ -38,7 +34,7 @@ export const createTrpcClient = <AppRouter extends AnyRouter>({
useFactory: () => {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore TODO: figure out why TS is complaining
return createTRPCProxyClient<AppRouter>({
return createTRPCRxJSProxyClient<AppRouter>({
transformer: options?.transformer,
links: [
...(options?.links ?? []),
Expand Down
113 changes: 113 additions & 0 deletions packages/trpc/src/lib/client/shared-internal.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
import {
AnyRouter,
ClientDataTransformerOptions,
CombinedDataTransformer,
DataTransformerOptions,
DefaultDataTransformer,
} from '@trpc/server';
import {
OperationContext,
OperationLink,
OperationResultObservable,
} from '@trpc/client/src/links/types';
import { Operation, TRPCLink } from '@trpc/client';
import { observable } from '@trpc/server/observable';

// Removed subscription
export type TRPCType = 'query' | 'mutation';

// Removed subscription and requestAsPromise
export type UntypedClientProperties =
| 'links'
| 'runtime'
| 'requestId'
| '$request'
| 'query'
| 'mutation';

/*
* One to one copy of the trpc client internal code
* Nothing was changed, but we can not import these methods because
* they are not exported
*/
export type IntersectionError<TKey extends string> =
`The property '${TKey}' in your router collides with a built-in method, rename this router or procedure on your backend.`;

export interface TRPCRequestOptions {
/**
* Pass additional context to links
*/
context?: OperationContext;
}

export function createChain<
TRouter extends AnyRouter,
TInput = unknown,
TOutput = unknown
>(opts: {
links: OperationLink<TRouter, TInput, TOutput>[];
op: Operation<TInput>;
}): OperationResultObservable<TRouter, TOutput> {
return observable((observer) => {
function execute(index = 0, op = opts.op) {
const next = opts.links[index];
if (!next) {
throw new Error(
'No more links to execute - did you forget to add an ending link?'
);
}
const subscription = next({
op,
next(nextOp) {
const nextObserver = execute(index + 1, nextOp);

return nextObserver;
},
});
return subscription;
}

const obs$ = execute();
return obs$.subscribe(observer);
});
}

export type CreateTRPCClientOptions<TRouter extends AnyRouter> =
| CreateTRPCClientBaseOptions<TRouter> & {
links: TRPCLink<TRouter>[];
};

export type CreateTRPCClientBaseOptions<TRouter extends AnyRouter> =
TRouter['_def']['_config']['transformer'] extends DefaultDataTransformer
? {
/**
* Data transformer
*
* You must use the same transformer on the backend and frontend
* @link https://trpc.io/docs/data-transformers
**/
transformer?: 'You must set a transformer on the backend router';
}
: TRouter['_def']['_config']['transformer'] extends DataTransformerOptions
? {
/**
* Data transformer
*
* You must use the same transformer on the backend and frontend
* @link https://trpc.io/docs/data-transformers
**/
transformer: TRouter['_def']['_config']['transformer'] extends CombinedDataTransformer
? DataTransformerOptions
: TRouter['_def']['_config']['transformer'];
}
: {
/**
* Data transformer
*
* You must use the same transformer on the backend and frontend
* @link https://trpc.io/docs/data-transformers
**/
transformer?:
| /** @deprecated **/ ClientDataTransformerOptions
| CombinedDataTransformer;
};
Loading

0 comments on commit 9749309

Please sign in to comment.