Skip to content
tRPC

Raw sockets

serveSocket serves the router over TCP, TLS or Unix sockets, with experimental_socketLink on the client.

Between services, or where there is no HTTP stack, serveSocket(handler) from @trpc12/trpc/socket serves the router on Effect's SocketServer. The platform layer picks the socket: NodeSocketServer.layer({ port }) for TCP, NodeSocketServer.layerTls for TLS, or { path } for a Unix socket.

import { import NodeRuntimeNodeRuntime, import NodeSocketServerNodeSocketServer } from '@effect/platform-node';
import { import LayerLayer } from 'effect';
import { function serveSocket(handler: SocketHandlerLike, opts?: SocketOptions): Layer.Layer<never, SocketServerError, SocketServer>serveSocket } from '@trpc12/trpc/socket';

function serveSocket(handler: SocketHandlerLike, opts?: SocketOptions): Layer.Layer<never, SocketServerError, SocketServer>serveSocket(const handler: FetchHandler<undefined>handler).Pipeable.pipe<Layer.Layer<never, SocketServerError, SocketServer>, Layer.Layer<never, SocketServerError, never>, Effect<never, SocketServerError, never>, void>(this: Layer.Layer<...>, ab: (_: Layer.Layer<never, SocketServerError, SocketServer>) => Layer.Layer<never, SocketServerError, never>, bc: (_: Layer.Layer<never, SocketServerError, never>) => Effect<never, SocketServerError, never>, cd: (_: Effect<...>) => void): void (+21 overloads)pipe(
  import LayerLayer.const provide: <never, SocketServerError, SocketServer>(that: Layer.Layer<SocketServer, SocketServerError, never>) => <RIn2, E2, ROut2>(self: Layer.Layer<ROut2, E2, RIn2>) => Layer.Layer<ROut2, SocketServerError | E2, Exclude<RIn2, SocketServer>> (+3 overloads)provide(import NodeSocketServerNodeSocketServer.const layer: (options: ServerOpts & ListenOptions) => Layer.Layer<SocketServer, SocketServerError>layer({ ListenOptions.port?: number | undefinedport: 4000 })),
  import LayerLayer.const launch: <RIn, E, ROut>(self: Layer.Layer<ROut, E, RIn>) => Effect<never, E, RIn>launch,
  import NodeRuntimeNodeRuntime.
const runMain: {
    (options?: {
        readonly disableErrorReporting?: boolean | undefined;
        readonly teardown?: Teardown | undefined;
    }): <E, A>(effect: Effect<A, E>) => void;
    <E, A>(effect: Effect<A, E>, options?: {
        readonly disableErrorReporting?: boolean | undefined;
        readonly teardown?: Teardown | undefined;
    }): void;
}
runMain
,
);

The client is experimental_socketLink with an Effect Socket:

import { import NodeSocketNodeSocket } from '@effect/platform-node';
import { function experimental_socketLink(opts: RawSocketLinkOptions): SocketLinkexperimental_socketLink } from '@trpc12/trpc/client';

const const link: SocketLink<{}>link = function experimental_socketLink(opts: RawSocketLinkOptions): SocketLinkexperimental_socketLink({
  RawSocketLinkOptions.socket: Effect<Socket, unknown, never>socket: import NodeSocketNodeSocket.
const makeNet: (options: NetConnectOpts & {
    readonly openTimeout?: Input | undefined;
}) => Effect<Socket>
makeNet
({ TcpSocketConnectOpts.host?: string | undefinedhost: 'localhost', TcpSocketConnectOpts.port: numberport: 4000 }),
SocketLinkOptions.connectionParams?: Record<string, unknown> | (() => MaybePromise<Record<string, unknown> | undefined>) | undefinedconnectionParams: { token: stringtoken }, });

A connection speaks the WebSocket protocol, one JSON message per line, so everything that works over wsLink works here: connectionParams, subscriptions with resume, deferred values, aborts, keep-alive pings and reconnects. createContext gets info.transport === 'socket'. There are no headers, so authenticate with connectionParams. A connection that sends nothing for 60 s is closed (serveSocket(handler, { idleTimeoutMs }), or false).

WebTransport isn't supported: no server runtime that Effect has a platform for serves it yet.