HTTP-prenumerationslänk
Denna sida har översatts av PageTurner AI (beta). Inte officiellt godkänd av projektet. Hittade du ett fel? Rapportera problem →
httpSubscriptionLink är en avslutande länk som använder Server-sent Events (SSE) för prenumerationer.
SSE är ett bra alternativ för realtidskommunikation eftersom det är lite enklare än att sätta upp en WebSockets-server.
Installation
Om din klientmiljö inte stöder EventSource behöver du en EventSource-polyfill. För React Native-specifika instruktioner, se kompatibilitetsavsnittet.
För att använda httpSubscriptionLink måste du använda en splitLink för att explicit ange att vi vill använda SSE för prenumerationer.
client/index.tstsimport {createTRPCClient ,httpBatchLink ,httpSubscriptionLink ,loggerLink ,splitLink ,} from '@trpc/client';import type {AppRouter } from './server';consttrpcClient =createTRPCClient <AppRouter >({/*** @see https://trpc.io/docs/v11/client/links*/links : [// adds pretty logs to your console in development and logs errors in productionloggerLink (),splitLink ({// uses the httpSubscriptionLink for subscriptionscondition : (op ) =>op .type === 'subscription',true :httpSubscriptionLink ({url : `/api/trpc`,}),false :httpBatchLink ({url : `/api/trpc`,}),}),],});
client/index.tstsimport {createTRPCClient ,httpBatchLink ,httpSubscriptionLink ,loggerLink ,splitLink ,} from '@trpc/client';import type {AppRouter } from './server';consttrpcClient =createTRPCClient <AppRouter >({/*** @see https://trpc.io/docs/v11/client/links*/links : [// adds pretty logs to your console in development and logs errors in productionloggerLink (),splitLink ({// uses the httpSubscriptionLink for subscriptionscondition : (op ) =>op .type === 'subscription',true :httpSubscriptionLink ({url : `/api/trpc`,}),false :httpBatchLink ({url : `/api/trpc`,}),}),],});
Det här dokumentet beskriver de specifika detaljerna för httpSubscriptionLink. För generell användning av prenumerationer, se vår prenumerationsguide.
Headers samt autentisering/auktorisering
Webbapplikationer
Samma domän
I en webbapplikation skickas kakor automatiskt som del av förfrågorna om klienten finns på samma domän som servern.
Tvärdomän
Om klienten och servern inte är på samma domän kan du använda withCredentials: true (läs mer på MDN).
Exempel:
tsx// [...]httpSubscriptionLink ({url : 'https://example.com/api/trpc',eventSourceOptions () {return {withCredentials : true, // <---};},});
tsx// [...]httpSubscriptionLink ({url : 'https://example.com/api/trpc',eventSourceOptions () {return {withCredentials : true, // <---};},});
Anpassade headers via ponyfill
Rekommenderas för icke-webbmiljöer
Du kan använda en ponyfill för EventSource och använda eventSourceOptions-callbacken för att lägga till headers.
tsximport {createTRPCClient ,httpBatchLink ,httpSubscriptionLink ,splitLink ,} from '@trpc/client';import {EventSourcePolyfill } from 'event-source-polyfill';import type {AppRouter } from './server';// Initialize the tRPC clientconsttrpc =createTRPCClient <AppRouter >({links : [splitLink ({condition : (op ) =>op .type === 'subscription',true :httpSubscriptionLink ({url : 'http://localhost:3000',// ponyfill EventSourceEventSource :EventSourcePolyfill ,// options to pass to the EventSourcePolyfill constructoreventSourceOptions : async ({op }) => {// you can use this to generate a signature for the operationconstsignature = awaitgetSignature (op );return {headers : {authorization : 'Bearer supersecret','x-signature':signature ,},};},}),false :httpBatchLink ({url : 'http://localhost:3000',}),}),],});
tsximport {createTRPCClient ,httpBatchLink ,httpSubscriptionLink ,splitLink ,} from '@trpc/client';import {EventSourcePolyfill } from 'event-source-polyfill';import type {AppRouter } from './server';// Initialize the tRPC clientconsttrpc =createTRPCClient <AppRouter >({links : [splitLink ({condition : (op ) =>op .type === 'subscription',true :httpSubscriptionLink ({url : 'http://localhost:3000',// ponyfill EventSourceEventSource :EventSourcePolyfill ,// options to pass to the EventSourcePolyfill constructoreventSourceOptions : async ({op }) => {// you can use this to generate a signature for the operationconstsignature = awaitgetSignature (op );return {headers : {authorization : 'Bearer supersecret','x-signature':signature ,},};},}),false :httpBatchLink ({url : 'http://localhost:3000',}),}),],});
Uppdatera konfiguration på aktiv anslutning
httpSubscriptionLink använder SSE via EventSource, vilket säkerställer att anslutningar som stöter på fel som nätverksproblem eller felaktiga statuskoder automatiskt försöker återansluta. Dock tillåter EventSource inte att eventSourceOptions() eller url() körs igen för att uppdatera konfigurationen, vilket är särskilt viktigt när autentisering har gått ut sedan senaste anslutningen.
För att lösa denna begränsning kan du använda en retryLink tillsammans med httpSubscriptionLink. Detta säkerställer att anslutningen återupprättas med senaste konfigurationen, inklusive uppdaterade autentiseringsuppgifter.
Observera att omstart av anslutningen kommer att återskapa EventSource från grunden, vilket innebär att tidigare spårade händelser går förlorade.
tsximport {createTRPCClient ,httpBatchLink ,httpSubscriptionLink ,retryLink ,splitLink ,} from '@trpc/client';import {EventSourcePolyfill ,EventSourcePolyfillInit ,} from 'event-source-polyfill';import type {AppRouter } from './server';// Initialize the tRPC clientconsttrpc =createTRPCClient <AppRouter >({links : [splitLink ({condition : (op ) =>op .type === 'subscription',false :httpBatchLink ({url : 'http://localhost:3000',}),true : [retryLink ({retry : (opts ) => {opts .op .type ;// ^? will always be 'subscription' since we're in a splitLinkconstcode =opts .error .data ?.code ;if (!code ) {// This shouldn't happen as our httpSubscriptionLink will automatically retry within when there's a non-parsable responseconsole .error ('No error code found, retrying',opts );return true;}if (code === 'UNAUTHORIZED' ||code === 'FORBIDDEN') {console .log ('Retrying due to 401/403 error');return true;}return false;},}),httpSubscriptionLink ({url : async () => {// calculate the latest URL if needed...returngetAuthenticatedUri ();},// ponyfill EventSourceEventSource :EventSourcePolyfill ,eventSourceOptions : async () => {// ...or maybe renew an access tokenconsttoken = awaitauth .getOrRenewToken ();return {headers : {authorization : `Bearer ${token }`,},};},}),],}),],});
tsximport {createTRPCClient ,httpBatchLink ,httpSubscriptionLink ,retryLink ,splitLink ,} from '@trpc/client';import {EventSourcePolyfill ,EventSourcePolyfillInit ,} from 'event-source-polyfill';import type {AppRouter } from './server';// Initialize the tRPC clientconsttrpc =createTRPCClient <AppRouter >({links : [splitLink ({condition : (op ) =>op .type === 'subscription',false :httpBatchLink ({url : 'http://localhost:3000',}),true : [retryLink ({retry : (opts ) => {opts .op .type ;// ^? will always be 'subscription' since we're in a splitLinkconstcode =opts .error .data ?.code ;if (!code ) {// This shouldn't happen as our httpSubscriptionLink will automatically retry within when there's a non-parsable responseconsole .error ('No error code found, retrying',opts );return true;}if (code === 'UNAUTHORIZED' ||code === 'FORBIDDEN') {console .log ('Retrying due to 401/403 error');return true;}return false;},}),httpSubscriptionLink ({url : async () => {// calculate the latest URL if needed...returngetAuthenticatedUri ();},// ponyfill EventSourceEventSource :EventSourcePolyfill ,eventSourceOptions : async () => {// ...or maybe renew an access tokenconsttoken = awaitauth .getOrRenewToken ();return {headers : {authorization : `Bearer ${token }`,},};},}),],}),],});
Anslutningsparametrar
För att autentisera med EventSource kan du definiera connectionParams i httpSubscriptionLink. Dessa parametrar skickas som del av URL:en, vilket är anledningen till att andra metoder föredras.
server/context.tstsimport type {CreateHTTPContextOptions } from '@trpc/server/adapters/standalone';export constcreateContext = async (opts :CreateHTTPContextOptions ) => {consttoken =opts .info .connectionParams ?.token ;// [... authenticate]return {};};export typeContext =Awaited <ReturnType <typeofcreateContext >>;
server/context.tstsimport type {CreateHTTPContextOptions } from '@trpc/server/adapters/standalone';export constcreateContext = async (opts :CreateHTTPContextOptions ) => {consttoken =opts .info .connectionParams ?.token ;// [... authenticate]return {};};export typeContext =Awaited <ReturnType <typeofcreateContext >>;
client/trpc.tstsimport {createTRPCClient ,httpBatchLink ,httpSubscriptionLink ,splitLink ,} from '@trpc/client';import type {AppRouter } from './server';// Initialize the tRPC clientconsttrpc =createTRPCClient <AppRouter >({links : [splitLink ({condition : (op ) =>op .type === 'subscription',true :httpSubscriptionLink ({url : 'http://localhost:3000',connectionParams : async () => {// Will be serialized as part of the URLreturn {token : 'supersecret',};},}),false :httpBatchLink ({url : 'http://localhost:3000',}),}),],});
client/trpc.tstsimport {createTRPCClient ,httpBatchLink ,httpSubscriptionLink ,splitLink ,} from '@trpc/client';import type {AppRouter } from './server';// Initialize the tRPC clientconsttrpc =createTRPCClient <AppRouter >({links : [splitLink ({condition : (op ) =>op .type === 'subscription',true :httpSubscriptionLink ({url : 'http://localhost:3000',connectionParams : async () => {// Will be serialized as part of the URLreturn {token : 'supersecret',};},}),false :httpBatchLink ({url : 'http://localhost:3000',}),}),],});
Timeout-konfiguration
httpSubscriptionLink stöder konfiguration av timeout vid inaktivitet via alternativet reconnectAfterInactivityMs. Om inga meddelanden (inklusive ping-meddelanden) tas emot inom den angivna tidsperioden markeras anslutningen som "ansluter" och kommer automatiskt försöka återansluta.
Timeout-konfigurationen sätts på serversidan vid initiering av tRPC:
server/trpc.tstsimport {initTRPC } from '@trpc/server';export constt =initTRPC .create ({sse : {client : {reconnectAfterInactivityMs : 3_000,},},});
server/trpc.tstsimport {initTRPC } from '@trpc/server';export constt =initTRPC .create ({sse : {client : {reconnectAfterInactivityMs : 3_000,},},});
Server-ping-konfiguration
Servern kan konfigureras att skicka periodiska ping-meddelanden för att hålla anslutningen vid liv och undvika timeout-avbrott. Detta är särskilt användbart i kombination med reconnectAfterInactivityMs.
server/trpc.tstsimport {initTRPC } from '@trpc/server';export constt =initTRPC .create ({sse : {// Maximum duration of a single SSE connection in milliseconds// maxDurationMs: 60_000,ping : {// Enable periodic ping messages to keep connection aliveenabled : true,// Send ping message every 2sintervalMs : 2_000,},// client: {// reconnectAfterInactivityMs: 3_000// }},});
server/trpc.tstsimport {initTRPC } from '@trpc/server';export constt =initTRPC .create ({sse : {// Maximum duration of a single SSE connection in milliseconds// maxDurationMs: 60_000,ping : {// Enable periodic ping messages to keep connection aliveenabled : true,// Send ping message every 2sintervalMs : 2_000,},// client: {// reconnectAfterInactivityMs: 3_000// }},});
Kompatibilitet (React Native)
httpSubscriptionLink använder EventSource-API:et, Streams API och AsyncIterators. Dessa stöds inte inbyggt i React Native och måste därför polyfillas.
För att ponyfilla EventSource rekommenderar vi att använda en polyfill som utnyttjar React Natives nätverksbibliotek framför polyfills som använder XMLHttpRequest-API:et. Bibliotek som polyfyllar EventSource med XMLHttpRequest återansluter inte när appen har varit i bakgrunden. Överväg att använda paketet rn-eventsource-reborn.
Streams API kan polyfillas med hjälp av paketet web-streams-polyfill.
AsyncIterators kan polyfillas med hjälp av paketet @azure/core-asynciterator-polyfill.
Installation
Installera de nödvändiga polyfills:
- npm
- yarn
- pnpm
- bun
- deno
npm install rn-eventsource-reborn web-streams-polyfill @azure/core-asynciterator-polyfill
yarn add rn-eventsource-reborn web-streams-polyfill @azure/core-asynciterator-polyfill
pnpm add rn-eventsource-reborn web-streams-polyfill @azure/core-asynciterator-polyfill
bun add rn-eventsource-reborn web-streams-polyfill @azure/core-asynciterator-polyfill
deno add npm:rn-eventsource-reborn npm:web-streams-polyfill npm:@azure/core-asynciterator-polyfill
Lägg till polyfills i ditt projekt innan länken används (t.ex. där du lägger till din TRPCReact.Provider):
utils/api.tsxtsimport '@azure/core-asynciterator-polyfill';import { RNEventSource } from 'rn-eventsource-reborn';import { ReadableStream, TransformStream } from 'web-streams-polyfill';globalThis.ReadableStream = globalThis.ReadableStream || ReadableStream;globalThis.TransformStream = globalThis.TransformStream || TransformStream;
utils/api.tsxtsimport '@azure/core-asynciterator-polyfill';import { RNEventSource } from 'rn-eventsource-reborn';import { ReadableStream, TransformStream } from 'web-streams-polyfill';globalThis.ReadableStream = globalThis.ReadableStream || ReadableStream;globalThis.TransformStream = globalThis.TransformStream || TransformStream;
När polyfills är tillagda kan du fortsätta att konfigurera httpSubscriptionLink enligt beskrivningen i avsnittet om installation.
Alternativ för httpSubscriptionLink
tstypeHTTPSubscriptionLinkOptions <TRoot extendsAnyClientTypes ,TEventSource extendsEventSourceLike .AnyConstructor = typeofEventSource ,> = {/*** The URL to connect to (can be a function that returns a URL)*/url : string | (() => string |Promise <string>);/*** Connection params that are available in `createContext()`* Serialized as part of the URL under the `connectionParams` query parameter*/connectionParams ?:|Record <string, string>| null| (() =>|Record <string, string>| null|Promise <Record <string, string> | null>);/*** Data transformer* @see https://trpc.io/docs/v11/data-transformers*/transformer ?:DataTransformerOptions ;/*** EventSource ponyfill*/EventSource ?:TEventSource ;/*** EventSource options or a callback that returns them*/eventSourceOptions ?:|EventSourceLike .InitDictOf <TEventSource >| ((opts : {op :Operation ;}) =>|EventSourceLike .InitDictOf <TEventSource >|Promise <EventSourceLike .InitDictOf <TEventSource >>);};
tstypeHTTPSubscriptionLinkOptions <TRoot extendsAnyClientTypes ,TEventSource extendsEventSourceLike .AnyConstructor = typeofEventSource ,> = {/*** The URL to connect to (can be a function that returns a URL)*/url : string | (() => string |Promise <string>);/*** Connection params that are available in `createContext()`* Serialized as part of the URL under the `connectionParams` query parameter*/connectionParams ?:|Record <string, string>| null| (() =>|Record <string, string>| null|Promise <Record <string, string> | null>);/*** Data transformer* @see https://trpc.io/docs/v11/data-transformers*/transformer ?:DataTransformerOptions ;/*** EventSource ponyfill*/EventSource ?:TEventSource ;/*** EventSource options or a callback that returns them*/eventSourceOptions ?:|EventSourceLike .InitDictOf <TEventSource >| ((opts : {op :Operation ;}) =>|EventSourceLike .InitDictOf <TEventSource >|Promise <EventSourceLike .InitDictOf <TEventSource >>);};
SSE-alternativ på servern
tsexport interfaceSSEStreamProducerOptions <TValue = unknown> {ping ?: {/*** Enable ping comments sent from the server* @default false*/enabled : boolean;/*** Interval in milliseconds* @default 1000*/intervalMs ?: number;};/*** Maximum duration in milliseconds for the request before ending the stream* @default undefined*/maxDurationMs ?: number;/*** End the request immediately after data is sent* Only useful for serverless runtimes that do not support streaming responses* @default false*/emitAndEndImmediately ?: boolean;/*** Client-specific options - these will be sent to the client as part of the first message* @default {}*/client ?: {/*** Timeout and reconnect after inactivity in milliseconds* @default undefined*/reconnectAfterInactivityMs ?: number;};}
tsexport interfaceSSEStreamProducerOptions <TValue = unknown> {ping ?: {/*** Enable ping comments sent from the server* @default false*/enabled : boolean;/*** Interval in milliseconds* @default 1000*/intervalMs ?: number;};/*** Maximum duration in milliseconds for the request before ending the stream* @default undefined*/maxDurationMs ?: number;/*** End the request immediately after data is sent* Only useful for serverless runtimes that do not support streaming responses* @default false*/emitAndEndImmediately ?: boolean;/*** Client-specific options - these will be sent to the client as part of the first message* @default {}*/client ?: {/*** Timeout and reconnect after inactivity in milliseconds* @default undefined*/reconnectAfterInactivityMs ?: number;};}