<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/">
    <channel>
        <title>tRPC Blog</title>
        <link>https://trpc.io/sv/blog</link>
        <description>tRPC Blog</description>
        <lastBuildDate>Fri, 21 Mar 2025 00:00:00 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <language>sv</language>
        <item>
            <title><![CDATA[Vi presenterar tRPC v11]]></title>
            <link>https://trpc.io/sv/blog/announcing-trpc-v11</link>
            <guid>https://trpc.io/sv/blog/announcing-trpc-v11</guid>
            <pubDate>Fri, 21 Mar 2025 00:00:00 GMT</pubDate>
            <description><![CDATA[Denna sida har översatts av PageTurner AI (beta). Inte officiellt godkänd av projektet.]]></description>
            <content:encoded><![CDATA[<div class="theme-admonition theme-admonition-info admonition_v5Ag alert alert--info"><div class="admonitionHeading_usrK"><span class="admonitionIcon_bgEp"><svg viewBox="0 0 14 16"><path fill-rule="evenodd" d="M7 2.3c3.14 0 5.7 2.56 5.7 5.7s-2.56 5.7-5.7 5.7A5.71 5.71 0 0 1 1.3 8c0-3.14 2.56-5.7 5.7-5.7zM7 1C3.14 1 0 4.14 0 8s3.14 7 7 7 7-3.14 7-7-3.14-7-7-7zm1 3H6v5h2V4zm0 6H6v2h2v-2z"></path></svg></span>Inofficiell Beta-översättning</div><div class="admonitionContent_e2NW"><p>Denna sida har översatts av <a href="https://page-turner.com/" target="_blank" rel="noopener noreferrer"><strong>PageTurner</strong></a> AI (beta). Inte officiellt godkänd av projektet.
Hittade du ett fel? <a href="https://feedback.page-turner.com/?repo_id=683d130a-1828-4b22-91cd-ef2d269ef3f5&amp;file_path=blog%2F2025-03-21-announcing-trpc-11.mdx&amp;locale=sv" target="_blank" rel="noopener noreferrer">Rapportera problem →</a></p></div></div>
<!-- -->
<p>Även om tRPC v11 länge varit produktionsredo via <code>@next</code>-taggen har vi fastnat i att lägga till nya funktioner utan att strikt hålla oss till semantisk versionering. Idag är vi glada att äntligen kunna dra av plåstret och meddela den officiella releasen av tRPC v11!</p>
<!-- -->
<p>Sedan vårt senaste majorversionssläpp i november 2022 har tRPC-gemenskapen vuxit avsevärt:</p>
<ul>
<li>
<p>Vi har nu över <a href="https://github.com/trpc/trpc" target="_blank" rel="noopener noreferrer">35 000 stjärnor på GitHub</a></p>
</li>
<li>
<p><a href="https://trpc.io/discord" target="_blank" rel="noopener noreferrer">En Discord-gemenskap</a> med mer än 5 000 medlemmar</p>
</li>
<li>
<p><a href="https://www.npmjs.com/package/@trpc/server" target="_blank" rel="noopener noreferrer">700k+ veckovisa npm-nedladdningar</a></p>
</li>
<li>
<p><a href="https://github.com/trpc/trpc/graphs/contributors" target="_blank" rel="noopener noreferrer">Hundratals bidragsgivare</a></p>
</li>
<li>
<p><a href="https://trpc.io/awesome" target="_blank" rel="noopener noreferrer">Ett <em>fantastiskt</em> ekosystem av tillägg, exempel och innehåll</a></p>
</li>
</ul>
<p>Med lanseringen av tRPC v11 är vi glada att dela med oss att v11 redan används i produktion av många stora TypeScript-projekt tack vare dess stabila utveckling på vår <code>@next</code>-kanal</p>
<p>För nya projekt kan du komma igång med ett <a href="https://trpc.io/awesome#-starting-points-example-projects-etc" target="_blank" rel="noopener noreferrer">exempelprogram</a> för att lära dig om tRPC v11. För projekt som fortfarande kör tRPC v10 <a href="https://trpc.io/docs/v11/migrate-from-v10-to-v11" target="_blank" rel="noopener noreferrer">besök migrationsguiden för v11</a>.</p>
<h2 class="anchor anchorWithStickyNavbar_MYIC" id="översikt-över-förändringar">Översikt över förändringar<a href="https://trpc.io/sv/blog/announcing-trpc-v11#%C3%B6versikt-%C3%B6ver-f%C3%B6r%C3%A4ndringar" class="hash-link" aria-label="Direktlänk till Översikt över förändringar" title="Direktlänk till Översikt över förändringar">​</a></h2>
<p>v11 är till stor del bakåtkompatibel med v10, men den innehåller många nya funktioner och förbättringar. Här är höjdpunkterna:</p>
<h3 class="anchor anchorWithStickyNavbar_MYIC" id="stöd-för-tanstack-query-v5">Stöd för TanStack Query v5<a href="https://trpc.io/sv/blog/announcing-trpc-v11#st%C3%B6d-f%C3%B6r-tanstack-query-v5" class="hash-link" aria-label="Direktlänk till Stöd för TanStack Query v5" title="Direktlänk till Stöd för TanStack Query v5">​</a></h3>
<p>När <a href="https://tanstack.com/blog/announcing-tanstack-query-v5" target="_blank" rel="noopener noreferrer">TanStack Query v5</a> släpptes krävdes några brytande ändringar i tRPC<!-- -->:s<!-- --> react-query-integration. Detta har funnits tillgängligt ganska länge via <code>@next</code>-taggen, men nu är det officiellt släppt. Många projekt har redan valt att uppgradera och njuter av fullt React Suspense-stöd samt många andra förbättringar. För råd om att migrera din tRPC React-klientkod kan du följa <a href="https://tanstack.com/query/v5/docs/react/guides/migrating-to-v5" target="_blank" rel="noopener noreferrer">TanStack Querys egen migrationsguide</a>.</p>
<h3 class="anchor anchorWithStickyNavbar_MYIC" id="ny-tanstack-react-query-integration">Ny TanStack React Query-integration<a href="https://trpc.io/sv/blog/announcing-trpc-v11#ny-tanstack-react-query-integration" class="hash-link" aria-label="Direktlänk till Ny TanStack React Query-integration" title="Direktlänk till Ny TanStack React Query-integration">​</a></h3>
<p><a href="https://trpc.io/sv/blog/introducing-tanstack-react-query-client">Se blogginlägget</a></p>
<h3 class="anchor anchorWithStickyNavbar_MYIC" id="stöd-för-formdata--icke-json-innehållstyper">Stöd för FormData / icke-JSON-innehållstyper<a href="https://trpc.io/sv/blog/announcing-trpc-v11#st%C3%B6d-f%C3%B6r-formdata--icke-json-inneh%C3%A5llstyper" class="hash-link" aria-label="Direktlänk till Stöd för FormData / icke-JSON-innehållstyper" title="Direktlänk till Stöd för FormData / icke-JSON-innehållstyper">​</a></h3>
<p>En av våra mest efterfrågade funktioner är möjligheten att skicka och ta emot mer än bara JSON-data. Med tRPC v11 kan du nu skicka och ta emot data i olika <a href="https://trpc.io/sv/docs/server/non-json-content-types">icke-JSON-innehållstyper</a>, <code>FormData</code>, och binära typer som <code>Blob</code>, <code>File</code> och <code>Uint8Array</code>. Du hittar ett exempel på hur du använder dessa innehållstyper <a href="https://github.com/trpc/trpc/tree/next/examples/minimal-content-types" target="_blank" rel="noopener noreferrer">här</a></p>
<div><pre class="shiki min-light twoslash lsp" style="background-color: #ffffff; color: #24292eff"><div class="language-id">ts</div><div class="code-container"><code><div class="line"><span style="color: #D32F2F">import</span><span style="color: #24292EFF"> { <data-lsp lsp="(alias) const publicProcedure: ProcedureBuilder<object, object, object, UnsetMarker, UnsetMarker, UnsetMarker, UnsetMarker, false>
import publicProcedure">publicProcedure</data-lsp></span><span style="color: #212121">,</span><span style="color: #24292EFF"> <data-lsp lsp="(alias) const router: RouterBuilder<{
    ctx: object;
    meta: object;
    errorShape: DefaultErrorShape;
    transformer: false;
}>
import router">router</data-lsp> } </span><span style="color: #D32F2F">from</span><span style="color: #24292EFF"> </span><span style="color: #22863A">'./trpc'</span><span style="color: #24292EFF">;</span></div><div class="line"><span style="color: #D32F2F">import</span><span style="color: #24292EFF"> { <data-lsp lsp="(alias) const octetInputParser: UtilityParser<OctetInput, ReadableStream<any>>
import octetInputParser">octetInputParser</data-lsp> } </span><span style="color: #D32F2F">from</span><span style="color: #24292EFF"> </span><span style="color: #22863A">'@trpc/server/http'</span><span style="color: #24292EFF">;</span></div><div class="line"><span style="color: #D32F2F">import</span><span style="color: #24292EFF"> { <data-lsp lsp="import z">z</data-lsp> } </span><span style="color: #D32F2F">from</span><span style="color: #24292EFF"> </span><span style="color: #22863A">'zod'</span><span style="color: #24292EFF">;</span></div><div class="line">&nbsp;</div><div class="line"><span style="color: #D32F2F">const</span><span style="color: #24292EFF"> </span><span style="color: #1976D2"><data-lsp lsp="const appRouter: BuiltRouter<{
    ctx: object;
    meta: object;
    errorShape: DefaultErrorShape;
    transformer: false;
}, DecorateCreateRouterOptions<{
    formData: MutationProcedure<{
        input: FormData;
        output: void;
        meta: object;
    }>;
    file: MutationProcedure<{
        input: OctetInput;
        output: void;
        meta: object;
    }>;
}>>">appRouter</data-lsp></span><span style="color: #24292EFF"> </span><span style="color: #D32F2F">=</span><span style="color: #24292EFF"> </span><span style="color: #6F42C1"><data-lsp lsp="(alias) router<{
    formData: MutationProcedure<{
        input: FormData;
        output: void;
        meta: object;
    }>;
    file: MutationProcedure<{
        input: OctetInput;
        output: void;
        meta: object;
    }>;
}>(_: {
    formData: MutationProcedure<{
        input: FormData;
        output: void;
        meta: object;
    }>;
    file: MutationProcedure<{
        input: OctetInput;
        output: void;
        meta: object;
    }>;
}): BuiltRouter<{
    ctx: object;
    meta: object;
    errorShape: DefaultErrorShape;
    transformer: false;
}, DecorateCreateRouterOptions<{
    formData: MutationProcedure<{
        input: FormData;
        output: void;
        meta: object;
    }>;
    file: MutationProcedure<{
        input: OctetInput;
        output: void;
        meta: object;
    }>;
}>>
import router">router</data-lsp></span><span style="color: #24292EFF">({</span></div><div class="line"><span style="color: #24292EFF">  <data-lsp lsp="(property) formData: MutationProcedure<{
    input: FormData;
    output: void;
    meta: object;
}>">formData</data-lsp></span><span style="color: #D32F2F">:</span><span style="color: #24292EFF"> <data-lsp lsp="(alias) const publicProcedure: ProcedureBuilder<object, object, object, UnsetMarker, UnsetMarker, UnsetMarker, UnsetMarker, false>
import publicProcedure">publicProcedure</data-lsp></span></div><div class="line"><span style="color: #24292EFF">    </span><span style="color: #6F42C1">.<data-lsp lsp="(method) ProcedureBuilder<object, object, object, UnsetMarker, UnsetMarker, UnsetMarker, UnsetMarker, false>.input<z.ZodCustom<FormData, FormData>>(schema: z.ZodCustom<FormData, FormData>): ProcedureBuilder<object, object, object, FormData, FormData, UnsetMarker, UnsetMarker, false>">input</data-lsp></span><span style="color: #24292EFF">(</span><span style="color: #1976D2"><data-lsp lsp="import z">z</data-lsp></span><span style="color: #6F42C1">.<data-lsp lsp="(alias) instanceof<{
    new (form?: HTMLFormElement, submitter?: HTMLElement | null): FormData;
    prototype: FormData;
}>(cls: {
    new (form?: HTMLFormElement, submitter?: HTMLElement | null): FormData;
    prototype: FormData;
}, params?: {
    when?: ((payload: z.core.ParsePayload) => boolean) | undefined | undefined;
    error?: string | z.core.$ZodErrorMap<z.core.$ZodIssueCustom> | undefined;
    message?: string | undefined | undefined;
}): z.ZodCustom<FormData, FormData>
export instanceof">instanceof</data-lsp></span><span style="color: #24292EFF">(<data-lsp lsp="var FormData: {
    new (form?: HTMLFormElement, submitter?: HTMLElement | null): FormData;
    prototype: FormData;
}">FormData</data-lsp>))</span></div><div class="line"><span style="color: #24292EFF">    </span><span style="color: #6F42C1">.<data-lsp lsp="(method) ProcedureBuilder<object, object, object, FormData, FormData, UnsetMarker, UnsetMarker, false>.mutation<void>(resolver: ProcedureResolver<object, object, object, FormData, UnsetMarker, void>): MutationProcedure<{
    input: FormData;
    output: void;
    meta: object;
}>">mutation</data-lsp></span><span style="color: #24292EFF">(</span><span style="color: #D32F2F">async</span><span style="color: #24292EFF"> ({ <data-lsp lsp="(parameter) input: FormData" style="border-bottom: solid 2px lightgrey;">input</data-lsp> }) </span><span style="color: #D32F2F">=&gt;</span><span style="color: #24292EFF"> {</span></div><div class="meta-line"><span class="popover-prefix">                        </span><span class="popover"><div class="arrow"></div>(parameter) input: FormData</span></div><div class="line"><span style="color: #24292EFF">    })</span><span style="color: #212121">,</span></div><div class="line"><span style="color: #24292EFF">  <data-lsp lsp="(property) file: MutationProcedure<{
    input: OctetInput;
    output: void;
    meta: object;
}>">file</data-lsp></span><span style="color: #D32F2F">:</span><span style="color: #24292EFF"> <data-lsp lsp="(alias) const publicProcedure: ProcedureBuilder<object, object, object, UnsetMarker, UnsetMarker, UnsetMarker, UnsetMarker, false>
import publicProcedure">publicProcedure</data-lsp></span></div><div class="line"><span style="color: #24292EFF">    </span><span style="color: #6F42C1">.<data-lsp lsp="(method) ProcedureBuilder<object, object, object, UnsetMarker, UnsetMarker, UnsetMarker, UnsetMarker, false>.input<UtilityParser<OctetInput, ReadableStream<any>>>(schema: UtilityParser<OctetInput, ReadableStream<any>>): ProcedureBuilder<object, object, object, OctetInput, ReadableStream<any>, UnsetMarker, UnsetMarker, false>">input</data-lsp></span><span style="color: #24292EFF">(<data-lsp lsp="(alias) const octetInputParser: UtilityParser<OctetInput, ReadableStream<any>>
import octetInputParser">octetInputParser</data-lsp>)</span></div><div class="line"><span style="color: #24292EFF">    </span><span style="color: #6F42C1">.<data-lsp lsp="(method) ProcedureBuilder<object, object, object, OctetInput, ReadableStream<any>, UnsetMarker, UnsetMarker, false>.mutation<void>(resolver: ProcedureResolver<object, object, object, ReadableStream<any>, UnsetMarker, void>): MutationProcedure<{
    input: OctetInput;
    output: void;
    meta: object;
}>">mutation</data-lsp></span><span style="color: #24292EFF">(</span><span style="color: #D32F2F">async</span><span style="color: #24292EFF"> ({ <data-lsp lsp="(parameter) input: ReadableStream<any>" style="border-bottom: solid 2px lightgrey;">input</data-lsp> }) </span><span style="color: #D32F2F">=&gt;</span><span style="color: #24292EFF"> {</span></div><div class="meta-line"><span class="popover-prefix">                        </span><span class="popover"><div class="arrow"></div>(parameter) input: ReadableStream&lt;any&gt;</span></div><div class="line"><span style="color: #24292EFF">    })</span><span style="color: #212121">,</span></div><div class="line"><span style="color: #24292EFF">});</span></div></code></div><button type="button" aria-label="Copy code to clipboard" class="copy-button" onclick="navigator.clipboard.writeText(this.previousSibling.innerText),this.classList.add(&quot;copied&quot;),this.textContent=&quot;Copied&quot;,setTimeout(()=>{this.classList.remove(&quot;copied&quot;),this.textContent=&quot;Copy&quot;},2e3)">Copy</button></pre>
<pre class="shiki github-dark twoslash lsp" style="background-color: #0d1117; color: #c9d1d9"><div class="language-id">ts</div><div class="code-container"><code><div class="line"><span style="color: #FF7B72">import</span><span style="color: #C9D1D9"> { <data-lsp lsp="(alias) const publicProcedure: ProcedureBuilder<object, object, object, UnsetMarker, UnsetMarker, UnsetMarker, UnsetMarker, false>
import publicProcedure">publicProcedure</data-lsp>, <data-lsp lsp="(alias) const router: RouterBuilder<{
    ctx: object;
    meta: object;
    errorShape: DefaultErrorShape;
    transformer: false;
}>
import router">router</data-lsp> } </span><span style="color: #FF7B72">from</span><span style="color: #C9D1D9"> </span><span style="color: #A5D6FF">'./trpc'</span><span style="color: #C9D1D9">;</span></div><div class="line"><span style="color: #FF7B72">import</span><span style="color: #C9D1D9"> { <data-lsp lsp="(alias) const octetInputParser: UtilityParser<OctetInput, ReadableStream<any>>
import octetInputParser">octetInputParser</data-lsp> } </span><span style="color: #FF7B72">from</span><span style="color: #C9D1D9"> </span><span style="color: #A5D6FF">'@trpc/server/http'</span><span style="color: #C9D1D9">;</span></div><div class="line"><span style="color: #FF7B72">import</span><span style="color: #C9D1D9"> { <data-lsp lsp="import z">z</data-lsp> } </span><span style="color: #FF7B72">from</span><span style="color: #C9D1D9"> </span><span style="color: #A5D6FF">'zod'</span><span style="color: #C9D1D9">;</span></div><div class="line">&nbsp;</div><div class="line"><span style="color: #FF7B72">const</span><span style="color: #C9D1D9"> </span><span style="color: #79C0FF"><data-lsp lsp="const appRouter: BuiltRouter<{
    ctx: object;
    meta: object;
    errorShape: DefaultErrorShape;
    transformer: false;
}, DecorateCreateRouterOptions<{
    formData: MutationProcedure<{
        input: FormData;
        output: void;
        meta: object;
    }>;
    file: MutationProcedure<{
        input: OctetInput;
        output: void;
        meta: object;
    }>;
}>>">appRouter</data-lsp></span><span style="color: #C9D1D9"> </span><span style="color: #FF7B72">=</span><span style="color: #C9D1D9"> </span><span style="color: #D2A8FF"><data-lsp lsp="(alias) router<{
    formData: MutationProcedure<{
        input: FormData;
        output: void;
        meta: object;
    }>;
    file: MutationProcedure<{
        input: OctetInput;
        output: void;
        meta: object;
    }>;
}>(_: {
    formData: MutationProcedure<{
        input: FormData;
        output: void;
        meta: object;
    }>;
    file: MutationProcedure<{
        input: OctetInput;
        output: void;
        meta: object;
    }>;
}): BuiltRouter<{
    ctx: object;
    meta: object;
    errorShape: DefaultErrorShape;
    transformer: false;
}, DecorateCreateRouterOptions<{
    formData: MutationProcedure<{
        input: FormData;
        output: void;
        meta: object;
    }>;
    file: MutationProcedure<{
        input: OctetInput;
        output: void;
        meta: object;
    }>;
}>>
import router">router</data-lsp></span><span style="color: #C9D1D9">({</span></div><div class="line"><span style="color: #C9D1D9">  <data-lsp lsp="(property) formData: MutationProcedure<{
    input: FormData;
    output: void;
    meta: object;
}>">formData</data-lsp>: <data-lsp lsp="(alias) const publicProcedure: ProcedureBuilder<object, object, object, UnsetMarker, UnsetMarker, UnsetMarker, UnsetMarker, false>
import publicProcedure">publicProcedure</data-lsp></span></div><div class="line"><span style="color: #C9D1D9">    .</span><span style="color: #D2A8FF"><data-lsp lsp="(method) ProcedureBuilder<object, object, object, UnsetMarker, UnsetMarker, UnsetMarker, UnsetMarker, false>.input<z.ZodCustom<FormData, FormData>>(schema: z.ZodCustom<FormData, FormData>): ProcedureBuilder<object, object, object, FormData, FormData, UnsetMarker, UnsetMarker, false>">input</data-lsp></span><span style="color: #C9D1D9">(<data-lsp lsp="import z">z</data-lsp>.</span><span style="color: #D2A8FF"><data-lsp lsp="(alias) instanceof<{
    new (form?: HTMLFormElement, submitter?: HTMLElement | null): FormData;
    prototype: FormData;
}>(cls: {
    new (form?: HTMLFormElement, submitter?: HTMLElement | null): FormData;
    prototype: FormData;
}, params?: {
    when?: ((payload: z.core.ParsePayload) => boolean) | undefined | undefined;
    error?: string | z.core.$ZodErrorMap<z.core.$ZodIssueCustom> | undefined;
    message?: string | undefined | undefined;
}): z.ZodCustom<FormData, FormData>
export instanceof">instanceof</data-lsp></span><span style="color: #C9D1D9">(<data-lsp lsp="var FormData: {
    new (form?: HTMLFormElement, submitter?: HTMLElement | null): FormData;
    prototype: FormData;
}">FormData</data-lsp>))</span></div><div class="line"><span style="color: #C9D1D9">    .</span><span style="color: #D2A8FF"><data-lsp lsp="(method) ProcedureBuilder<object, object, object, FormData, FormData, UnsetMarker, UnsetMarker, false>.mutation<void>(resolver: ProcedureResolver<object, object, object, FormData, UnsetMarker, void>): MutationProcedure<{
    input: FormData;
    output: void;
    meta: object;
}>">mutation</data-lsp></span><span style="color: #C9D1D9">(</span><span style="color: #FF7B72">async</span><span style="color: #C9D1D9"> ({ </span><span style="color: #FFA657"><data-lsp lsp="(parameter) input: FormData" style="border-bottom: solid 2px lightgrey;">input</data-lsp></span><span style="color: #C9D1D9"> }) </span><span style="color: #FF7B72">=&gt;</span><span style="color: #C9D1D9"> {</span></div><div class="meta-line"><span class="popover-prefix">                        </span><span class="popover"><div class="arrow"></div>(parameter) input: FormData</span></div><div class="line"><span style="color: #C9D1D9">    }),</span></div><div class="line"><span style="color: #C9D1D9">  <data-lsp lsp="(property) file: MutationProcedure<{
    input: OctetInput;
    output: void;
    meta: object;
}>">file</data-lsp>: <data-lsp lsp="(alias) const publicProcedure: ProcedureBuilder<object, object, object, UnsetMarker, UnsetMarker, UnsetMarker, UnsetMarker, false>
import publicProcedure">publicProcedure</data-lsp></span></div><div class="line"><span style="color: #C9D1D9">    .</span><span style="color: #D2A8FF"><data-lsp lsp="(method) ProcedureBuilder<object, object, object, UnsetMarker, UnsetMarker, UnsetMarker, UnsetMarker, false>.input<UtilityParser<OctetInput, ReadableStream<any>>>(schema: UtilityParser<OctetInput, ReadableStream<any>>): ProcedureBuilder<object, object, object, OctetInput, ReadableStream<any>, UnsetMarker, UnsetMarker, false>">input</data-lsp></span><span style="color: #C9D1D9">(<data-lsp lsp="(alias) const octetInputParser: UtilityParser<OctetInput, ReadableStream<any>>
import octetInputParser">octetInputParser</data-lsp>)</span></div><div class="line"><span style="color: #C9D1D9">    .</span><span style="color: #D2A8FF"><data-lsp lsp="(method) ProcedureBuilder<object, object, object, OctetInput, ReadableStream<any>, UnsetMarker, UnsetMarker, false>.mutation<void>(resolver: ProcedureResolver<object, object, object, ReadableStream<any>, UnsetMarker, void>): MutationProcedure<{
    input: OctetInput;
    output: void;
    meta: object;
}>">mutation</data-lsp></span><span style="color: #C9D1D9">(</span><span style="color: #FF7B72">async</span><span style="color: #C9D1D9"> ({ </span><span style="color: #FFA657"><data-lsp lsp="(parameter) input: ReadableStream<any>" style="border-bottom: solid 2px lightgrey;">input</data-lsp></span><span style="color: #C9D1D9"> }) </span><span style="color: #FF7B72">=&gt;</span><span style="color: #C9D1D9"> {</span></div><div class="meta-line"><span class="popover-prefix">                        </span><span class="popover"><div class="arrow"></div>(parameter) input: ReadableStream&lt;any&gt;</span></div><div class="line"><span style="color: #C9D1D9">    }),</span></div><div class="line"><span style="color: #C9D1D9">});</span></div></code></div><button type="button" aria-label="Copy code to clipboard" class="copy-button" onclick="navigator.clipboard.writeText(this.previousSibling.innerText),this.classList.add(&quot;copied&quot;),this.textContent=&quot;Copied&quot;,setTimeout(()=>{this.classList.remove(&quot;copied&quot;),this.textContent=&quot;Copy&quot;},2e3)">Copy</button></pre></div>
<h3 class="anchor anchorWithStickyNavbar_MYIC" id="react-server-components--nextjs-app-router">React Server Components / Next.js App Router<a href="https://trpc.io/sv/blog/announcing-trpc-v11#react-server-components--nextjs-app-router" class="hash-link" aria-label="Direktlänk till React Server Components / Next.js App Router" title="Direktlänk till React Server Components / Next.js App Router">​</a></h3>
<p>Även om du kunnat använda tRPC med Next.js App Router sedan dag ett, antingen genom att:</p>
<ul>
<li>
<p>använda ett servercentrerat tillvägagångssätt med async/await via antingen <a href="https://trpc.io/sv/docs/server/server-side-calls"><code>createCaller</code></a> eller <a href="https://trpc.io/sv/docs/client/vanilla"><code>createTRPCClient</code></a>,</p>
</li>
<li>
<p>eller ett klientcentrerat tillvägagångssätt med <a href="https://trpc.io/sv/docs/client/react">React Query-integrationen</a> och klientsidohooks,</p>
</li>
</ul>
<p>Bron mellan dessa två metoder har varit lite hackig. Valideringsmönster blir inkonsekventa när man måste hantera olika datahämtningsmetoder, vilket resulterar i en mindre optimal utvecklarupplevelse som inte lever upp till tRPC<!-- -->:s<!-- --> standard.</p>
<p>För att åtgärda detta har vi förbättrat stödet för React Server Components (RSC) och lagt till prefetch-hjälpmedel för att underlätta användandet av RSC<!-- -->:ers<!-- --> kraft som körs enbart på servern, i kombination med React Queries mycket dynamiska klientcache. Du kan nu köra en procedur på servern i en RSC, plocka upp det väntande löftet på klienten och automatiskt hydrera React Query-cachen på klientsidan. Detta gör att du kan bygga mycket dynamiska applikationer utan att drabbas av server-klient-vattenfall. Läs mer i vår <a href="https://trpc.io/sv/docs/client/react/server-components">dokumentation om Server Components</a>.</p>
<p>Utöver dessa nya prefetch-mönster har vi lagt till experimentellt stöd för serverfunktioner. Du kan läsa mer om detta i <a href="https://trpc.io/sv/blog/trpc-actions">vårt blogginlägg</a>. Vi planerar att fortsätta iterera på denna funktion när serverfunktioner blir en mer etablerad metod i ekosystemet.</p>
<p>Vi har också samarbetat med <a href="https://x.com/tannerlinsley/status/1844500352655335522" target="_blank" rel="noopener noreferrer">TanStack-teamet för att hjälpa till att designa API<!-- -->:er<!-- --> för deras serverfunktioner</a>. Vårt mål är att dela upp delar av tRPC<!-- -->:s<!-- --> kraftfulla mellanvarussystem i ett separat paket som kan användas i hela ekosystemet.</p>
<h3 class="anchor anchorWithStickyNavbar_MYIC" id="strömmade-svar-från-queries-och-mutationer">Strömmade svar från queries och mutationer<a href="https://trpc.io/sv/blog/announcing-trpc-v11#str%C3%B6mmade-svar-fr%C3%A5n-queries-och-mutationer" class="hash-link" aria-label="Direktlänk till Strömmade svar från queries och mutationer" title="Direktlänk till Strömmade svar från queries och mutationer">​</a></h3>
<p>Vi har introducerat <a href="https://trpc.io/sv/docs/client/links/httpBatchStreamLink">httpBatchStreamLink</a> som möjliggör strömmade svar från queries. Detta är användbart när du arbetar med stora dataset eller när du behöver bearbeta data i realtid och skicka dem till frontend under tiden. Detta är inte en ersättning för prenumerationer, utan ytterligare ett verktyg i din verktygslåda.</p>
<div><pre class="shiki min-light twoslash lsp" style="background-color: #ffffff; color: #24292eff"><div class="language-id">ts</div><div class="code-container"><code><div class="line"><span style="color: #C2C3C5">// @filename: server.ts</span></div><div class="line"><span style="color: #D32F2F">import</span><span style="color: #24292EFF"> { <data-lsp lsp="(alias) const publicProcedure: ProcedureBuilder<object, object, object, UnsetMarker, UnsetMarker, UnsetMarker, UnsetMarker, false>
import publicProcedure">publicProcedure</data-lsp></span><span style="color: #212121">,</span><span style="color: #24292EFF"> <data-lsp lsp="(alias) const router: RouterBuilder<{
    ctx: object;
    meta: object;
    errorShape: DefaultErrorShape;
    transformer: false;
}>
import router">router</data-lsp> } </span><span style="color: #D32F2F">from</span><span style="color: #24292EFF"> </span><span style="color: #22863A">'./trpc'</span><span style="color: #24292EFF">;</span></div><div class="line">&nbsp;</div><div class="line"><span style="color: #D32F2F">const</span><span style="color: #24292EFF"> </span><span style="color: #1976D2"><data-lsp lsp="const appRouter: BuiltRouter<{
    ctx: object;
    meta: object;
    errorShape: DefaultErrorShape;
    transformer: false;
}, DecorateCreateRouterOptions<{
    examples: {
        iterable: QueryProcedure<{
            input: void;
            output: AsyncGenerator<number, never, unknown>;
            meta: object;
        }>;
    };
}>>">appRouter</data-lsp></span><span style="color: #24292EFF"> </span><span style="color: #D32F2F">=</span><span style="color: #24292EFF"> </span><span style="color: #6F42C1"><data-lsp lsp="(alias) router<{
    examples: {
        iterable: QueryProcedure<{
            input: void;
            output: AsyncGenerator<number, never, unknown>;
            meta: object;
        }>;
    };
}>(_: {
    examples: {
        iterable: QueryProcedure<{
            input: void;
            output: AsyncGenerator<number, never, unknown>;
            meta: object;
        }>;
    };
}): BuiltRouter<{
    ctx: object;
    meta: object;
    errorShape: DefaultErrorShape;
    transformer: false;
}, DecorateCreateRouterOptions<{
    examples: {
        iterable: QueryProcedure<{
            input: void;
            output: AsyncGenerator<number, never, unknown>;
            meta: object;
        }>;
    };
}>>
import router">router</data-lsp></span><span style="color: #24292EFF">({</span></div><div class="line"><span style="color: #24292EFF">  <data-lsp lsp="(property) examples: {
    iterable: QueryProcedure<{
        input: void;
        output: AsyncGenerator<number, never, unknown>;
        meta: object;
    }>;
}">examples</data-lsp></span><span style="color: #D32F2F">:</span><span style="color: #24292EFF"> {</span></div><div class="line"><span style="color: #24292EFF">    <data-lsp lsp="(property) iterable: QueryProcedure<{
    input: void;
    output: AsyncGenerator<number, never, unknown>;
    meta: object;
}>">iterable</data-lsp></span><span style="color: #D32F2F">:</span><span style="color: #24292EFF"> </span><span style="color: #1976D2"><data-lsp lsp="(alias) const publicProcedure: ProcedureBuilder<object, object, object, UnsetMarker, UnsetMarker, UnsetMarker, UnsetMarker, false>
import publicProcedure">publicProcedure</data-lsp></span><span style="color: #6F42C1">.<data-lsp lsp="(method) ProcedureBuilder<object, object, object, UnsetMarker, UnsetMarker, UnsetMarker, UnsetMarker, false>.query<AsyncGenerator<number, never, unknown>>(resolver: ProcedureResolver<object, object, object, UnsetMarker, UnsetMarker, AsyncGenerator<number, never, unknown>>): QueryProcedure<{
    input: void;
    output: AsyncGenerator<number, never, unknown>;
    meta: object;
}>">query</data-lsp></span><span style="color: #24292EFF">(</span><span style="color: #D32F2F">async</span><span style="color: #24292EFF"> </span><span style="color: #D32F2F">function*</span><span style="color: #24292EFF"> () {</span></div><div class="line"><span style="color: #24292EFF">      </span><span style="color: #D32F2F">let</span><span style="color: #24292EFF"> <data-lsp lsp="let i: number">i</data-lsp> </span><span style="color: #D32F2F">=</span><span style="color: #24292EFF"> </span><span style="color: #1976D2">0</span><span style="color: #24292EFF">;</span></div><div class="line"><span style="color: #24292EFF">      </span><span style="color: #D32F2F">while</span><span style="color: #24292EFF"> (</span><span style="color: #1976D2">true</span><span style="color: #24292EFF">) {</span></div><div class="line"><span style="color: #24292EFF">        </span><span style="color: #D32F2F">await</span><span style="color: #24292EFF"> </span><span style="color: #D32F2F">new</span><span style="color: #24292EFF"> </span><span style="color: #1976D2"><data-lsp lsp="var Promise: PromiseConstructor
new <unknown>(executor: (resolve: (value: unknown) => void, reject: (reason?: any) => void) => void) => Promise<unknown>">Promise</data-lsp></span><span style="color: #24292EFF">((<data-lsp lsp="(parameter) resolve: (value: unknown) => void">resolve</data-lsp>) </span><span style="color: #D32F2F">=&gt;</span><span style="color: #24292EFF"> </span><span style="color: #6F42C1"><data-lsp lsp="function setTimeout(callback: (_: void) => void, delay?: number): NodeJS.Timeout (+2 overloads)
namespace setTimeout">setTimeout</data-lsp></span><span style="color: #24292EFF">(<data-lsp lsp="(parameter) resolve: (value: unknown) => void">resolve</data-lsp></span><span style="color: #212121">,</span><span style="color: #24292EFF"> </span><span style="color: #1976D2">500</span><span style="color: #24292EFF">));</span></div><div class="line"><span style="color: #24292EFF">        </span><span style="color: #D32F2F">yield</span><span style="color: #24292EFF"> <data-lsp lsp="let i: number">i</data-lsp></span><span style="color: #D32F2F">++</span><span style="color: #24292EFF">;</span></div><div class="line"><span style="color: #24292EFF">      }</span></div><div class="line"><span style="color: #24292EFF">    })</span><span style="color: #212121">,</span></div><div class="line"><span style="color: #24292EFF">  }</span><span style="color: #212121">,</span></div><div class="line"><span style="color: #24292EFF">});</span></div><div class="line">&nbsp;</div><div class="line"><span style="color: #D32F2F">export</span><span style="color: #24292EFF"> </span><span style="color: #D32F2F">type</span><span style="color: #24292EFF"> </span><span style="color: #6F42C1"><data-lsp lsp="type AppRouter = Router<{
    ctx: object;
    meta: object;
    errorShape: DefaultErrorShape;
    transformer: false;
}, DecorateCreateRouterOptions<{
    examples: {
        iterable: QueryProcedure<{
            input: void;
            output: AsyncGenerator<number, never, unknown>;
            meta: object;
        }>;
    };
}>> &amp; DecorateCreateRouterOptions<{
    examples: {
        iterable: QueryProcedure<{
            input: void;
            output: AsyncGenerator<number, never, unknown>;
            meta: object;
        }>;
    };
}>">AppRouter</data-lsp></span><span style="color: #24292EFF"> </span><span style="color: #D32F2F">=</span><span style="color: #24292EFF"> </span><span style="color: #D32F2F">typeof</span><span style="color: #24292EFF"> <data-lsp lsp="const appRouter: BuiltRouter<{
    ctx: object;
    meta: object;
    errorShape: DefaultErrorShape;
    transformer: false;
}, DecorateCreateRouterOptions<{
    examples: {
        iterable: QueryProcedure<{
            input: void;
            output: AsyncGenerator<number, never, unknown>;
            meta: object;
        }>;
    };
}>>">appRouter</data-lsp>;</span></div><div class="line">&nbsp;</div><div class="line">&nbsp;</div><div class="line"><span style="color: #C2C3C5">// @filename: client.ts</span></div><div class="line"><span style="color: #D32F2F">import</span><span style="color: #24292EFF"> { <data-lsp lsp="(alias) function createTRPCClient<TRouter extends AnyRouter>(opts: CreateTRPCClientOptions<TRouter>): TRPCClient<TRouter>
import createTRPCClient">createTRPCClient</data-lsp></span><span style="color: #212121">,</span><span style="color: #24292EFF"> <data-lsp lsp="(alias) function httpBatchStreamLink<TRouter extends AnyRouter>(opts: HTTPBatchLinkOptions<TRouter[&quot;_def&quot;][&quot;_config&quot;][&quot;$types&quot;]>): TRPCLink<TRouter>
import httpBatchStreamLink">httpBatchStreamLink</data-lsp> } </span><span style="color: #D32F2F">from</span><span style="color: #24292EFF"> </span><span style="color: #22863A">'@trpc/client'</span><span style="color: #24292EFF">;</span></div><div class="line"><span style="color: #D32F2F">import</span><span style="color: #24292EFF"> </span><span style="color: #D32F2F">type</span><span style="color: #24292EFF"> { <data-lsp lsp="(alias) type AppRouter = Router<{
    ctx: object;
    meta: object;
    errorShape: DefaultErrorShape;
    transformer: false;
}, DecorateCreateRouterOptions<{
    examples: {
        iterable: QueryProcedure<{
            input: void;
            output: AsyncGenerator<number, never, unknown>;
            meta: object;
        }>;
    };
}>> &amp; DecorateCreateRouterOptions<{
    examples: {
        iterable: QueryProcedure<{
            input: void;
            output: AsyncGenerator<number, never, unknown>;
            meta: object;
        }>;
    };
}>
import AppRouter">AppRouter</data-lsp> } </span><span style="color: #D32F2F">from</span><span style="color: #24292EFF"> </span><span style="color: #22863A">'./server'</span><span style="color: #24292EFF">;</span></div><div class="line">&nbsp;</div><div class="line"><span style="color: #D32F2F">const</span><span style="color: #24292EFF"> </span><span style="color: #1976D2"><data-lsp lsp="const trpc: TRPCClient<BuiltRouter<{
    ctx: object;
    meta: object;
    errorShape: DefaultErrorShape;
    transformer: false;
}, DecorateCreateRouterOptions<{
    examples: {
        iterable: QueryProcedure<{
            input: void;
            output: AsyncGenerator<number, never, unknown>;
            meta: object;
        }>;
    };
}>>>">trpc</data-lsp></span><span style="color: #24292EFF"> </span><span style="color: #D32F2F">=</span><span style="color: #24292EFF"> </span><span style="color: #6F42C1"><data-lsp lsp="(alias) createTRPCClient<BuiltRouter<{
    ctx: object;
    meta: object;
    errorShape: DefaultErrorShape;
    transformer: false;
}, DecorateCreateRouterOptions<{
    examples: {
        iterable: QueryProcedure<{
            input: void;
            output: AsyncGenerator<number, never, unknown>;
            meta: object;
        }>;
    };
}>>>(opts: CreateTRPCClientOptions<BuiltRouter<{
    ctx: object;
    meta: object;
    errorShape: DefaultErrorShape;
    transformer: false;
}, DecorateCreateRouterOptions<{
    examples: {
        iterable: QueryProcedure<{
            input: void;
            output: AsyncGenerator<number, never, unknown>;
            meta: object;
        }>;
    };
}>>>): TRPCClient<...>
import createTRPCClient">createTRPCClient</data-lsp></span><span style="color: #24292EFF">&lt;</span><span style="color: #6F42C1"><data-lsp lsp="(alias) type AppRouter = Router<{
    ctx: object;
    meta: object;
    errorShape: DefaultErrorShape;
    transformer: false;
}, DecorateCreateRouterOptions<{
    examples: {
        iterable: QueryProcedure<{
            input: void;
            output: AsyncGenerator<number, never, unknown>;
            meta: object;
        }>;
    };
}>> &amp; DecorateCreateRouterOptions<{
    examples: {
        iterable: QueryProcedure<{
            input: void;
            output: AsyncGenerator<number, never, unknown>;
            meta: object;
        }>;
    };
}>
import AppRouter">AppRouter</data-lsp></span><span style="color: #24292EFF">&gt;({</span></div><div class="line"><span style="color: #24292EFF">  <data-lsp lsp="(property) links: TRPCLink<BuiltRouter<{
    ctx: object;
    meta: object;
    errorShape: DefaultErrorShape;
    transformer: false;
}, DecorateCreateRouterOptions<{
    examples: {
        iterable: QueryProcedure<{
            input: void;
            output: AsyncGenerator<number, never, unknown>;
            meta: object;
        }>;
    };
}>>>[]">links</data-lsp></span><span style="color: #D32F2F">:</span><span style="color: #24292EFF"> [</span></div><div class="line"><span style="color: #24292EFF">    </span><span style="color: #6F42C1"><data-lsp lsp="(alias) httpBatchStreamLink<BuiltRouter<{
    ctx: object;
    meta: object;
    errorShape: DefaultErrorShape;
    transformer: false;
}, DecorateCreateRouterOptions<{
    examples: {
        iterable: QueryProcedure<{
            input: void;
            output: AsyncGenerator<number, never, unknown>;
            meta: object;
        }>;
    };
}>>>(opts: HTTPBatchLinkOptions<{
    ctx: object;
    meta: object;
    errorShape: DefaultErrorShape;
    transformer: false;
}>): TRPCLink<BuiltRouter<{
    ctx: object;
    meta: object;
    errorShape: DefaultErrorShape;
    transformer: false;
}, DecorateCreateRouterOptions<...>>>
import httpBatchStreamLink">httpBatchStreamLink</data-lsp></span><span style="color: #24292EFF">({</span></div><div class="line"><span style="color: #24292EFF">      <data-lsp lsp="(property) url: string | URL">url</data-lsp></span><span style="color: #D32F2F">:</span><span style="color: #24292EFF"> </span><span style="color: #22863A">'http://localhost:3000'</span><span style="color: #212121">,</span></div><div class="line"><span style="color: #24292EFF">    })</span><span style="color: #212121">,</span></div><div class="line"><span style="color: #24292EFF">  ]</span><span style="color: #212121">,</span></div><div class="line"><span style="color: #24292EFF">});</span></div><div class="line"><span style="color: #D32F2F">const</span><span style="color: #24292EFF"> </span><span style="color: #1976D2"><data-lsp lsp="const iterable: AsyncIterable<number, never, unknown>" style="border-bottom: solid 2px lightgrey;">iterable</data-lsp></span><span style="color: #24292EFF"> </span><span style="color: #D32F2F">=</span><span style="color: #24292EFF"> </span><span style="color: #D32F2F">await</span><span style="color: #24292EFF"> </span><span style="color: #1976D2"><data-lsp lsp="const trpc: TRPCClient<BuiltRouter<{
    ctx: object;
    meta: object;
    errorShape: DefaultErrorShape;
    transformer: false;
}, DecorateCreateRouterOptions<{
    examples: {
        iterable: QueryProcedure<{
            input: void;
            output: AsyncGenerator<number, never, unknown>;
            meta: object;
        }>;
    };
}>>>">trpc</data-lsp></span><span style="color: #6F42C1">.</span><span style="color: #1976D2"><data-lsp lsp="(property) examples: DecoratedProcedureRecord<{
    transformer: false;
    errorShape: DefaultErrorShape;
}, DecorateCreateRouterOptions<{
    iterable: QueryProcedure<{
        input: void;
        output: AsyncGenerator<number, never, unknown>;
        meta: object;
    }>;
}>>">examples</data-lsp></span><span style="color: #6F42C1">.</span><span style="color: #1976D2"><data-lsp lsp="(property) iterable: {
    query: Resolver<{
        input: void;
        output: AsyncIterable<number, never, unknown>;
        errorShape: DefaultErrorShape;
        transformer: false;
    }>;
}">iterable</data-lsp></span><span style="color: #6F42C1">.<data-lsp lsp="(property) query: (input: void, opts?: TRPCProcedureOptions) => Promise<AsyncIterable<number, never, unknown>>">query</data-lsp></span><span style="color: #24292EFF">();</span></div><div class="meta-line"><span class="popover-prefix">         </span><span class="popover"><div class="arrow"></div>const iterable: AsyncIterable&lt;number, never, unknown&gt;</span></div><div class="line">&nbsp;</div><div class="line"><span style="color: #D32F2F">for</span><span style="color: #24292EFF"> </span><span style="color: #D32F2F">await</span><span style="color: #24292EFF"> (</span><span style="color: #D32F2F">const</span><span style="color: #24292EFF"> </span><span style="color: #1976D2"><data-lsp lsp="const value: number">value</data-lsp></span><span style="color: #24292EFF"> </span><span style="color: #D32F2F">of</span><span style="color: #24292EFF"> <data-lsp lsp="const iterable: AsyncIterable<number, never, unknown>">iterable</data-lsp>) {</span></div><div class="line"><span style="color: #24292EFF">  </span><span style="color: #1976D2"><data-lsp lsp="namespace console
var console: Console">console</data-lsp></span><span style="color: #6F42C1">.<data-lsp lsp="(method) Console.log(message?: any, ...optionalParams: any[]): void (+1 overload)">log</data-lsp></span><span style="color: #24292EFF">(</span><span style="color: #22863A">'Iterable:'</span><span style="color: #212121">,</span><span style="color: #24292EFF"> <data-lsp lsp="const value: number" style="border-bottom: solid 2px lightgrey;">value</data-lsp>);</span></div><div class="meta-line"><span class="popover-prefix">                            </span><span class="popover"><div class="arrow"></div>const value: number</span></div><div class="line"><span style="color: #24292EFF">}</span></div></code></div><button type="button" aria-label="Copy code to clipboard" class="copy-button" onclick="navigator.clipboard.writeText(this.previousSibling.innerText),this.classList.add(&quot;copied&quot;),this.textContent=&quot;Copied&quot;,setTimeout(()=>{this.classList.remove(&quot;copied&quot;),this.textContent=&quot;Copy&quot;},2e3)">Copy</button></pre>
<pre class="shiki github-dark twoslash lsp" style="background-color: #0d1117; color: #c9d1d9"><div class="language-id">ts</div><div class="code-container"><code><div class="line"><span style="color: #8B949E">// @filename: server.ts</span></div><div class="line"><span style="color: #FF7B72">import</span><span style="color: #C9D1D9"> { <data-lsp lsp="(alias) const publicProcedure: ProcedureBuilder<object, object, object, UnsetMarker, UnsetMarker, UnsetMarker, UnsetMarker, false>
import publicProcedure">publicProcedure</data-lsp>, <data-lsp lsp="(alias) const router: RouterBuilder<{
    ctx: object;
    meta: object;
    errorShape: DefaultErrorShape;
    transformer: false;
}>
import router">router</data-lsp> } </span><span style="color: #FF7B72">from</span><span style="color: #C9D1D9"> </span><span style="color: #A5D6FF">'./trpc'</span><span style="color: #C9D1D9">;</span></div><div class="line">&nbsp;</div><div class="line"><span style="color: #FF7B72">const</span><span style="color: #C9D1D9"> </span><span style="color: #79C0FF"><data-lsp lsp="const appRouter: BuiltRouter<{
    ctx: object;
    meta: object;
    errorShape: DefaultErrorShape;
    transformer: false;
}, DecorateCreateRouterOptions<{
    examples: {
        iterable: QueryProcedure<{
            input: void;
            output: AsyncGenerator<number, never, unknown>;
            meta: object;
        }>;
    };
}>>">appRouter</data-lsp></span><span style="color: #C9D1D9"> </span><span style="color: #FF7B72">=</span><span style="color: #C9D1D9"> </span><span style="color: #D2A8FF"><data-lsp lsp="(alias) router<{
    examples: {
        iterable: QueryProcedure<{
            input: void;
            output: AsyncGenerator<number, never, unknown>;
            meta: object;
        }>;
    };
}>(_: {
    examples: {
        iterable: QueryProcedure<{
            input: void;
            output: AsyncGenerator<number, never, unknown>;
            meta: object;
        }>;
    };
}): BuiltRouter<{
    ctx: object;
    meta: object;
    errorShape: DefaultErrorShape;
    transformer: false;
}, DecorateCreateRouterOptions<{
    examples: {
        iterable: QueryProcedure<{
            input: void;
            output: AsyncGenerator<number, never, unknown>;
            meta: object;
        }>;
    };
}>>
import router">router</data-lsp></span><span style="color: #C9D1D9">({</span></div><div class="line"><span style="color: #C9D1D9">  <data-lsp lsp="(property) examples: {
    iterable: QueryProcedure<{
        input: void;
        output: AsyncGenerator<number, never, unknown>;
        meta: object;
    }>;
}">examples</data-lsp>: {</span></div><div class="line"><span style="color: #C9D1D9">    <data-lsp lsp="(property) iterable: QueryProcedure<{
    input: void;
    output: AsyncGenerator<number, never, unknown>;
    meta: object;
}>">iterable</data-lsp>: <data-lsp lsp="(alias) const publicProcedure: ProcedureBuilder<object, object, object, UnsetMarker, UnsetMarker, UnsetMarker, UnsetMarker, false>
import publicProcedure">publicProcedure</data-lsp>.</span><span style="color: #D2A8FF"><data-lsp lsp="(method) ProcedureBuilder<object, object, object, UnsetMarker, UnsetMarker, UnsetMarker, UnsetMarker, false>.query<AsyncGenerator<number, never, unknown>>(resolver: ProcedureResolver<object, object, object, UnsetMarker, UnsetMarker, AsyncGenerator<number, never, unknown>>): QueryProcedure<{
    input: void;
    output: AsyncGenerator<number, never, unknown>;
    meta: object;
}>">query</data-lsp></span><span style="color: #C9D1D9">(</span><span style="color: #FF7B72">async</span><span style="color: #C9D1D9"> </span><span style="color: #FF7B72">function*</span><span style="color: #C9D1D9"> () {</span></div><div class="line"><span style="color: #C9D1D9">      </span><span style="color: #FF7B72">let</span><span style="color: #C9D1D9"> <data-lsp lsp="let i: number">i</data-lsp> </span><span style="color: #FF7B72">=</span><span style="color: #C9D1D9"> </span><span style="color: #79C0FF">0</span><span style="color: #C9D1D9">;</span></div><div class="line"><span style="color: #C9D1D9">      </span><span style="color: #FF7B72">while</span><span style="color: #C9D1D9"> (</span><span style="color: #79C0FF">true</span><span style="color: #C9D1D9">) {</span></div><div class="line"><span style="color: #C9D1D9">        </span><span style="color: #FF7B72">await</span><span style="color: #C9D1D9"> </span><span style="color: #FF7B72">new</span><span style="color: #C9D1D9"> </span><span style="color: #79C0FF"><data-lsp lsp="var Promise: PromiseConstructor
new <unknown>(executor: (resolve: (value: unknown) => void, reject: (reason?: any) => void) => void) => Promise<unknown>">Promise</data-lsp></span><span style="color: #C9D1D9">((</span><span style="color: #FFA657"><data-lsp lsp="(parameter) resolve: (value: unknown) => void">resolve</data-lsp></span><span style="color: #C9D1D9">) </span><span style="color: #FF7B72">=&gt;</span><span style="color: #C9D1D9"> </span><span style="color: #79C0FF"><data-lsp lsp="function setTimeout(callback: (_: void) => void, delay?: number): NodeJS.Timeout (+2 overloads)
namespace setTimeout">setTimeout</data-lsp></span><span style="color: #C9D1D9">(<data-lsp lsp="(parameter) resolve: (value: unknown) => void">resolve</data-lsp>, </span><span style="color: #79C0FF">500</span><span style="color: #C9D1D9">));</span></div><div class="line"><span style="color: #C9D1D9">        </span><span style="color: #FF7B72">yield</span><span style="color: #C9D1D9"> <data-lsp lsp="let i: number">i</data-lsp></span><span style="color: #FF7B72">++</span><span style="color: #C9D1D9">;</span></div><div class="line"><span style="color: #C9D1D9">      }</span></div><div class="line"><span style="color: #C9D1D9">    }),</span></div><div class="line"><span style="color: #C9D1D9">  },</span></div><div class="line"><span style="color: #C9D1D9">});</span></div><div class="line">&nbsp;</div><div class="line"><span style="color: #FF7B72">export</span><span style="color: #C9D1D9"> </span><span style="color: #FF7B72">type</span><span style="color: #C9D1D9"> </span><span style="color: #FFA657"><data-lsp lsp="type AppRouter = Router<{
    ctx: object;
    meta: object;
    errorShape: DefaultErrorShape;
    transformer: false;
}, DecorateCreateRouterOptions<{
    examples: {
        iterable: QueryProcedure<{
            input: void;
            output: AsyncGenerator<number, never, unknown>;
            meta: object;
        }>;
    };
}>> &amp; DecorateCreateRouterOptions<{
    examples: {
        iterable: QueryProcedure<{
            input: void;
            output: AsyncGenerator<number, never, unknown>;
            meta: object;
        }>;
    };
}>">AppRouter</data-lsp></span><span style="color: #C9D1D9"> </span><span style="color: #FF7B72">=</span><span style="color: #C9D1D9"> </span><span style="color: #FF7B72">typeof</span><span style="color: #C9D1D9"> <data-lsp lsp="const appRouter: BuiltRouter<{
    ctx: object;
    meta: object;
    errorShape: DefaultErrorShape;
    transformer: false;
}, DecorateCreateRouterOptions<{
    examples: {
        iterable: QueryProcedure<{
            input: void;
            output: AsyncGenerator<number, never, unknown>;
            meta: object;
        }>;
    };
}>>">appRouter</data-lsp>;</span></div><div class="line">&nbsp;</div><div class="line">&nbsp;</div><div class="line"><span style="color: #8B949E">// @filename: client.ts</span></div><div class="line"><span style="color: #FF7B72">import</span><span style="color: #C9D1D9"> { <data-lsp lsp="(alias) function createTRPCClient<TRouter extends AnyRouter>(opts: CreateTRPCClientOptions<TRouter>): TRPCClient<TRouter>
import createTRPCClient">createTRPCClient</data-lsp>, <data-lsp lsp="(alias) function httpBatchStreamLink<TRouter extends AnyRouter>(opts: HTTPBatchLinkOptions<TRouter[&quot;_def&quot;][&quot;_config&quot;][&quot;$types&quot;]>): TRPCLink<TRouter>
import httpBatchStreamLink">httpBatchStreamLink</data-lsp> } </span><span style="color: #FF7B72">from</span><span style="color: #C9D1D9"> </span><span style="color: #A5D6FF">'@trpc/client'</span><span style="color: #C9D1D9">;</span></div><div class="line"><span style="color: #FF7B72">import</span><span style="color: #C9D1D9"> </span><span style="color: #FF7B72">type</span><span style="color: #C9D1D9"> { <data-lsp lsp="(alias) type AppRouter = Router<{
    ctx: object;
    meta: object;
    errorShape: DefaultErrorShape;
    transformer: false;
}, DecorateCreateRouterOptions<{
    examples: {
        iterable: QueryProcedure<{
            input: void;
            output: AsyncGenerator<number, never, unknown>;
            meta: object;
        }>;
    };
}>> &amp; DecorateCreateRouterOptions<{
    examples: {
        iterable: QueryProcedure<{
            input: void;
            output: AsyncGenerator<number, never, unknown>;
            meta: object;
        }>;
    };
}>
import AppRouter">AppRouter</data-lsp> } </span><span style="color: #FF7B72">from</span><span style="color: #C9D1D9"> </span><span style="color: #A5D6FF">'./server'</span><span style="color: #C9D1D9">;</span></div><div class="line">&nbsp;</div><div class="line"><span style="color: #FF7B72">const</span><span style="color: #C9D1D9"> </span><span style="color: #79C0FF"><data-lsp lsp="const trpc: TRPCClient<BuiltRouter<{
    ctx: object;
    meta: object;
    errorShape: DefaultErrorShape;
    transformer: false;
}, DecorateCreateRouterOptions<{
    examples: {
        iterable: QueryProcedure<{
            input: void;
            output: AsyncGenerator<number, never, unknown>;
            meta: object;
        }>;
    };
}>>>">trpc</data-lsp></span><span style="color: #C9D1D9"> </span><span style="color: #FF7B72">=</span><span style="color: #C9D1D9"> </span><span style="color: #D2A8FF"><data-lsp lsp="(alias) createTRPCClient<BuiltRouter<{
    ctx: object;
    meta: object;
    errorShape: DefaultErrorShape;
    transformer: false;
}, DecorateCreateRouterOptions<{
    examples: {
        iterable: QueryProcedure<{
            input: void;
            output: AsyncGenerator<number, never, unknown>;
            meta: object;
        }>;
    };
}>>>(opts: CreateTRPCClientOptions<BuiltRouter<{
    ctx: object;
    meta: object;
    errorShape: DefaultErrorShape;
    transformer: false;
}, DecorateCreateRouterOptions<{
    examples: {
        iterable: QueryProcedure<{
            input: void;
            output: AsyncGenerator<number, never, unknown>;
            meta: object;
        }>;
    };
}>>>): TRPCClient<...>
import createTRPCClient">createTRPCClient</data-lsp></span><span style="color: #C9D1D9">&lt;</span><span style="color: #FFA657"><data-lsp lsp="(alias) type AppRouter = Router<{
    ctx: object;
    meta: object;
    errorShape: DefaultErrorShape;
    transformer: false;
}, DecorateCreateRouterOptions<{
    examples: {
        iterable: QueryProcedure<{
            input: void;
            output: AsyncGenerator<number, never, unknown>;
            meta: object;
        }>;
    };
}>> &amp; DecorateCreateRouterOptions<{
    examples: {
        iterable: QueryProcedure<{
            input: void;
            output: AsyncGenerator<number, never, unknown>;
            meta: object;
        }>;
    };
}>
import AppRouter">AppRouter</data-lsp></span><span style="color: #C9D1D9">&gt;({</span></div><div class="line"><span style="color: #C9D1D9">  <data-lsp lsp="(property) links: TRPCLink<BuiltRouter<{
    ctx: object;
    meta: object;
    errorShape: DefaultErrorShape;
    transformer: false;
}, DecorateCreateRouterOptions<{
    examples: {
        iterable: QueryProcedure<{
            input: void;
            output: AsyncGenerator<number, never, unknown>;
            meta: object;
        }>;
    };
}>>>[]">links</data-lsp>: [</span></div><div class="line"><span style="color: #C9D1D9">    </span><span style="color: #D2A8FF"><data-lsp lsp="(alias) httpBatchStreamLink<BuiltRouter<{
    ctx: object;
    meta: object;
    errorShape: DefaultErrorShape;
    transformer: false;
}, DecorateCreateRouterOptions<{
    examples: {
        iterable: QueryProcedure<{
            input: void;
            output: AsyncGenerator<number, never, unknown>;
            meta: object;
        }>;
    };
}>>>(opts: HTTPBatchLinkOptions<{
    ctx: object;
    meta: object;
    errorShape: DefaultErrorShape;
    transformer: false;
}>): TRPCLink<BuiltRouter<{
    ctx: object;
    meta: object;
    errorShape: DefaultErrorShape;
    transformer: false;
}, DecorateCreateRouterOptions<...>>>
import httpBatchStreamLink">httpBatchStreamLink</data-lsp></span><span style="color: #C9D1D9">({</span></div><div class="line"><span style="color: #C9D1D9">      <data-lsp lsp="(property) url: string | URL">url</data-lsp>: </span><span style="color: #A5D6FF">'http://localhost:3000'</span><span style="color: #C9D1D9">,</span></div><div class="line"><span style="color: #C9D1D9">    }),</span></div><div class="line"><span style="color: #C9D1D9">  ],</span></div><div class="line"><span style="color: #C9D1D9">});</span></div><div class="line"><span style="color: #FF7B72">const</span><span style="color: #C9D1D9"> </span><span style="color: #79C0FF"><data-lsp lsp="const iterable: AsyncIterable<number, never, unknown>" style="border-bottom: solid 2px lightgrey;">iterable</data-lsp></span><span style="color: #C9D1D9"> </span><span style="color: #FF7B72">=</span><span style="color: #C9D1D9"> </span><span style="color: #FF7B72">await</span><span style="color: #C9D1D9"> <data-lsp lsp="const trpc: TRPCClient<BuiltRouter<{
    ctx: object;
    meta: object;
    errorShape: DefaultErrorShape;
    transformer: false;
}, DecorateCreateRouterOptions<{
    examples: {
        iterable: QueryProcedure<{
            input: void;
            output: AsyncGenerator<number, never, unknown>;
            meta: object;
        }>;
    };
}>>>">trpc</data-lsp>.<data-lsp lsp="(property) examples: DecoratedProcedureRecord<{
    transformer: false;
    errorShape: DefaultErrorShape;
}, DecorateCreateRouterOptions<{
    iterable: QueryProcedure<{
        input: void;
        output: AsyncGenerator<number, never, unknown>;
        meta: object;
    }>;
}>>">examples</data-lsp>.<data-lsp lsp="(property) iterable: {
    query: Resolver<{
        input: void;
        output: AsyncIterable<number, never, unknown>;
        errorShape: DefaultErrorShape;
        transformer: false;
    }>;
}">iterable</data-lsp>.</span><span style="color: #D2A8FF"><data-lsp lsp="(property) query: (input: void, opts?: TRPCProcedureOptions) => Promise<AsyncIterable<number, never, unknown>>">query</data-lsp></span><span style="color: #C9D1D9">();</span></div><div class="meta-line"><span class="popover-prefix">         </span><span class="popover"><div class="arrow"></div>const iterable: AsyncIterable&lt;number, never, unknown&gt;</span></div><div class="line">&nbsp;</div><div class="line"><span style="color: #FF7B72">for</span><span style="color: #C9D1D9"> </span><span style="color: #FF7B72">await</span><span style="color: #C9D1D9"> (</span><span style="color: #FF7B72">const</span><span style="color: #C9D1D9"> </span><span style="color: #79C0FF"><data-lsp lsp="const value: number">value</data-lsp></span><span style="color: #C9D1D9"> </span><span style="color: #FF7B72">of</span><span style="color: #C9D1D9"> <data-lsp lsp="const iterable: AsyncIterable<number, never, unknown>">iterable</data-lsp>) {</span></div><div class="line"><span style="color: #C9D1D9">  <data-lsp lsp="namespace console
var console: Console">console</data-lsp>.</span><span style="color: #D2A8FF"><data-lsp lsp="(method) Console.log(message?: any, ...optionalParams: any[]): void (+1 overload)">log</data-lsp></span><span style="color: #C9D1D9">(</span><span style="color: #A5D6FF">'Iterable:'</span><span style="color: #C9D1D9">, <data-lsp lsp="const value: number" style="border-bottom: solid 2px lightgrey;">value</data-lsp>);</span></div><div class="meta-line"><span class="popover-prefix">                            </span><span class="popover"><div class="arrow"></div>const value: number</span></div><div class="line"><span style="color: #C9D1D9">}</span></div></code></div><button type="button" aria-label="Copy code to clipboard" class="copy-button" onclick="navigator.clipboard.writeText(this.previousSibling.innerText),this.classList.add(&quot;copied&quot;),this.textContent=&quot;Copied&quot;,setTimeout(()=>{this.classList.remove(&quot;copied&quot;),this.textContent=&quot;Copy&quot;},2e3)">Copy</button></pre></div>
<h3 class="anchor anchorWithStickyNavbar_MYIC" id="förenklade-routerdefinitioner">Förenklade routerdefinitioner<a href="https://trpc.io/sv/blog/announcing-trpc-v11#f%C3%B6renklade-routerdefinitioner" class="hash-link" aria-label="Direktlänk till Förenklade routerdefinitioner" title="Direktlänk till Förenklade routerdefinitioner">​</a></h3>
<p>Vi har infört en ny förkortad syntax för att definiera din router, vilket förenklar processen att skapa dina routes. <a href="https://trpc.io/sv/docs/server/routers#inline-sub-router">Dokumentation</a></p>
<div><pre class="shiki min-light twoslash lsp" style="background-color: #ffffff; color: #24292eff"><div class="language-id">ts</div><div class="code-container"><code><div class="line"><span style="color: #D32F2F">const</span><span style="color: #24292EFF"> </span><span style="color: #1976D2"><data-lsp lsp="const appRouter: BuiltRouter<{
    ctx: object;
    meta: object;
    errorShape: DefaultErrorShape;
    transformer: false;
}, DecorateCreateRouterOptions<{
    nested1: {
        proc: QueryProcedure<{
            input: void;
            output: string;
            meta: object;
        }>;
    };
    nested2: BuiltRouter<{
        ctx: object;
        meta: object;
        errorShape: DefaultErrorShape;
        transformer: false;
    }, DecorateCreateRouterOptions<{
        proc: QueryProcedure<{
            input: void;
            output: string;
            meta: object;
        }>;
    }>>;
}>>">appRouter</data-lsp></span><span style="color: #24292EFF"> </span><span style="color: #D32F2F">=</span><span style="color: #24292EFF"> </span><span style="color: #6F42C1"><data-lsp lsp="const router: RouterBuilder
<{
    nested1: {
        proc: QueryProcedure<{
            input: void;
            output: string;
            meta: object;
        }>;
    };
    nested2: BuiltRouter<{
        ctx: object;
        meta: object;
        errorShape: DefaultErrorShape;
        transformer: false;
    }, DecorateCreateRouterOptions<{
        proc: QueryProcedure<{
            input: void;
            output: string;
            meta: object;
        }>;
    }>>;
}>(_: {
    nested1: {
        proc: QueryProcedure<{
            input: void;
            output: string;
            meta: object;
        }>;
    };
    nested2: BuiltRouter<{
        ctx: object;
        meta: object;
        errorShape: DefaultErrorShape;
        transformer: false;
    }, DecorateCreateRouterOptions<{
        proc: QueryProcedure<{
            input: void;
            output: string;
            meta: object;
        }>;
    }>>;
}) => BuiltRouter<...>">router</data-lsp></span><span style="color: #24292EFF">({</span></div><div class="line"><span style="color: #24292EFF">  </span><span style="color: #C2C3C5">// Shorthand plain object for creating a sub-router</span></div><div class="line"><span style="color: #24292EFF">  <data-lsp lsp="(property) nested1: {
    proc: QueryProcedure<{
        input: void;
        output: string;
        meta: object;
    }>;
}">nested1</data-lsp></span><span style="color: #D32F2F">:</span><span style="color: #24292EFF"> {</span></div><div class="line"><span style="color: #24292EFF">    <data-lsp lsp="(property) proc: QueryProcedure<{
    input: void;
    output: string;
    meta: object;
}>">proc</data-lsp></span><span style="color: #D32F2F">:</span><span style="color: #24292EFF"> </span><span style="color: #1976D2"><data-lsp lsp="const publicProcedure: ProcedureBuilder<object, object, object, UnsetMarker, UnsetMarker, UnsetMarker, UnsetMarker, false>">publicProcedure</data-lsp></span><span style="color: #6F42C1">.<data-lsp lsp="(method) ProcedureBuilder<object, object, object, UnsetMarker, UnsetMarker, UnsetMarker, UnsetMarker, false>.query<string>(resolver: ProcedureResolver<object, object, object, UnsetMarker, UnsetMarker, string>): QueryProcedure<{
    input: void;
    output: string;
    meta: object;
}>">query</data-lsp></span><span style="color: #24292EFF">(() </span><span style="color: #D32F2F">=&gt;</span><span style="color: #24292EFF"> </span><span style="color: #22863A">'...'</span><span style="color: #24292EFF">)</span><span style="color: #212121">,</span></div><div class="line"><span style="color: #24292EFF">  }</span><span style="color: #212121">,</span></div><div class="line"><span style="color: #24292EFF">  </span><span style="color: #C2C3C5">// Equivalent of:</span></div><div class="line"><span style="color: #24292EFF">  <data-lsp lsp="(property) nested2: BuiltRouter<{
    ctx: object;
    meta: object;
    errorShape: DefaultErrorShape;
    transformer: false;
}, DecorateCreateRouterOptions<{
    proc: QueryProcedure<{
        input: void;
        output: string;
        meta: object;
    }>;
}>>">nested2</data-lsp></span><span style="color: #D32F2F">:</span><span style="color: #24292EFF"> </span><span style="color: #6F42C1"><data-lsp lsp="const router: RouterBuilder
<{
    proc: QueryProcedure<{
        input: void;
        output: string;
        meta: object;
    }>;
}>(_: {
    proc: QueryProcedure<{
        input: void;
        output: string;
        meta: object;
    }>;
}) => BuiltRouter<{
    ctx: object;
    meta: object;
    errorShape: DefaultErrorShape;
    transformer: false;
}, DecorateCreateRouterOptions<{
    proc: QueryProcedure<{
        input: void;
        output: string;
        meta: object;
    }>;
}>>">router</data-lsp></span><span style="color: #24292EFF">({</span></div><div class="line"><span style="color: #24292EFF">    <data-lsp lsp="(property) proc: QueryProcedure<{
    input: void;
    output: string;
    meta: object;
}>">proc</data-lsp></span><span style="color: #D32F2F">:</span><span style="color: #24292EFF"> </span><span style="color: #1976D2"><data-lsp lsp="const publicProcedure: ProcedureBuilder<object, object, object, UnsetMarker, UnsetMarker, UnsetMarker, UnsetMarker, false>">publicProcedure</data-lsp></span><span style="color: #6F42C1">.<data-lsp lsp="(method) ProcedureBuilder<object, object, object, UnsetMarker, UnsetMarker, UnsetMarker, UnsetMarker, false>.query<string>(resolver: ProcedureResolver<object, object, object, UnsetMarker, UnsetMarker, string>): QueryProcedure<{
    input: void;
    output: string;
    meta: object;
}>">query</data-lsp></span><span style="color: #24292EFF">(() </span><span style="color: #D32F2F">=&gt;</span><span style="color: #24292EFF"> </span><span style="color: #22863A">'...'</span><span style="color: #24292EFF">)</span><span style="color: #212121">,</span></div><div class="line"><span style="color: #24292EFF">  })</span><span style="color: #212121">,</span></div><div class="line"><span style="color: #24292EFF">});</span></div></code></div><button type="button" aria-label="Copy code to clipboard" class="copy-button" onclick="navigator.clipboard.writeText(this.previousSibling.innerText),this.classList.add(&quot;copied&quot;),this.textContent=&quot;Copied&quot;,setTimeout(()=>{this.classList.remove(&quot;copied&quot;),this.textContent=&quot;Copy&quot;},2e3)">Copy</button></pre>
<pre class="shiki github-dark twoslash lsp" style="background-color: #0d1117; color: #c9d1d9"><div class="language-id">ts</div><div class="code-container"><code><div class="line"><span style="color: #FF7B72">const</span><span style="color: #C9D1D9"> </span><span style="color: #79C0FF"><data-lsp lsp="const appRouter: BuiltRouter<{
    ctx: object;
    meta: object;
    errorShape: DefaultErrorShape;
    transformer: false;
}, DecorateCreateRouterOptions<{
    nested1: {
        proc: QueryProcedure<{
            input: void;
            output: string;
            meta: object;
        }>;
    };
    nested2: BuiltRouter<{
        ctx: object;
        meta: object;
        errorShape: DefaultErrorShape;
        transformer: false;
    }, DecorateCreateRouterOptions<{
        proc: QueryProcedure<{
            input: void;
            output: string;
            meta: object;
        }>;
    }>>;
}>>">appRouter</data-lsp></span><span style="color: #C9D1D9"> </span><span style="color: #FF7B72">=</span><span style="color: #C9D1D9"> </span><span style="color: #D2A8FF"><data-lsp lsp="const router: RouterBuilder
<{
    nested1: {
        proc: QueryProcedure<{
            input: void;
            output: string;
            meta: object;
        }>;
    };
    nested2: BuiltRouter<{
        ctx: object;
        meta: object;
        errorShape: DefaultErrorShape;
        transformer: false;
    }, DecorateCreateRouterOptions<{
        proc: QueryProcedure<{
            input: void;
            output: string;
            meta: object;
        }>;
    }>>;
}>(_: {
    nested1: {
        proc: QueryProcedure<{
            input: void;
            output: string;
            meta: object;
        }>;
    };
    nested2: BuiltRouter<{
        ctx: object;
        meta: object;
        errorShape: DefaultErrorShape;
        transformer: false;
    }, DecorateCreateRouterOptions<{
        proc: QueryProcedure<{
            input: void;
            output: string;
            meta: object;
        }>;
    }>>;
}) => BuiltRouter<...>">router</data-lsp></span><span style="color: #C9D1D9">({</span></div><div class="line"><span style="color: #C9D1D9">  </span><span style="color: #8B949E">// Shorthand plain object for creating a sub-router</span></div><div class="line"><span style="color: #C9D1D9">  <data-lsp lsp="(property) nested1: {
    proc: QueryProcedure<{
        input: void;
        output: string;
        meta: object;
    }>;
}">nested1</data-lsp>: {</span></div><div class="line"><span style="color: #C9D1D9">    <data-lsp lsp="(property) proc: QueryProcedure<{
    input: void;
    output: string;
    meta: object;
}>">proc</data-lsp>: <data-lsp lsp="const publicProcedure: ProcedureBuilder<object, object, object, UnsetMarker, UnsetMarker, UnsetMarker, UnsetMarker, false>">publicProcedure</data-lsp>.</span><span style="color: #D2A8FF"><data-lsp lsp="(method) ProcedureBuilder<object, object, object, UnsetMarker, UnsetMarker, UnsetMarker, UnsetMarker, false>.query<string>(resolver: ProcedureResolver<object, object, object, UnsetMarker, UnsetMarker, string>): QueryProcedure<{
    input: void;
    output: string;
    meta: object;
}>">query</data-lsp></span><span style="color: #C9D1D9">(() </span><span style="color: #FF7B72">=&gt;</span><span style="color: #C9D1D9"> </span><span style="color: #A5D6FF">'...'</span><span style="color: #C9D1D9">),</span></div><div class="line"><span style="color: #C9D1D9">  },</span></div><div class="line"><span style="color: #C9D1D9">  </span><span style="color: #8B949E">// Equivalent of:</span></div><div class="line"><span style="color: #C9D1D9">  <data-lsp lsp="(property) nested2: BuiltRouter<{
    ctx: object;
    meta: object;
    errorShape: DefaultErrorShape;
    transformer: false;
}, DecorateCreateRouterOptions<{
    proc: QueryProcedure<{
        input: void;
        output: string;
        meta: object;
    }>;
}>>">nested2</data-lsp>: </span><span style="color: #D2A8FF"><data-lsp lsp="const router: RouterBuilder
<{
    proc: QueryProcedure<{
        input: void;
        output: string;
        meta: object;
    }>;
}>(_: {
    proc: QueryProcedure<{
        input: void;
        output: string;
        meta: object;
    }>;
}) => BuiltRouter<{
    ctx: object;
    meta: object;
    errorShape: DefaultErrorShape;
    transformer: false;
}, DecorateCreateRouterOptions<{
    proc: QueryProcedure<{
        input: void;
        output: string;
        meta: object;
    }>;
}>>">router</data-lsp></span><span style="color: #C9D1D9">({</span></div><div class="line"><span style="color: #C9D1D9">    <data-lsp lsp="(property) proc: QueryProcedure<{
    input: void;
    output: string;
    meta: object;
}>">proc</data-lsp>: <data-lsp lsp="const publicProcedure: ProcedureBuilder<object, object, object, UnsetMarker, UnsetMarker, UnsetMarker, UnsetMarker, false>">publicProcedure</data-lsp>.</span><span style="color: #D2A8FF"><data-lsp lsp="(method) ProcedureBuilder<object, object, object, UnsetMarker, UnsetMarker, UnsetMarker, UnsetMarker, false>.query<string>(resolver: ProcedureResolver<object, object, object, UnsetMarker, UnsetMarker, string>): QueryProcedure<{
    input: void;
    output: string;
    meta: object;
}>">query</data-lsp></span><span style="color: #C9D1D9">(() </span><span style="color: #FF7B72">=&gt;</span><span style="color: #C9D1D9"> </span><span style="color: #A5D6FF">'...'</span><span style="color: #C9D1D9">),</span></div><div class="line"><span style="color: #C9D1D9">  }),</span></div><div class="line"><span style="color: #C9D1D9">});</span></div></code></div><button type="button" aria-label="Copy code to clipboard" class="copy-button" onclick="navigator.clipboard.writeText(this.previousSibling.innerText),this.classList.add(&quot;copied&quot;),this.textContent=&quot;Copied&quot;,setTimeout(()=>{this.classList.remove(&quot;copied&quot;),this.textContent=&quot;Copy&quot;},2e3)">Copy</button></pre></div>
<h3 class="anchor anchorWithStickyNavbar_MYIC" id="prenumerationer-server-sent-events-och-andra-förbättringar">Prenumerationer: Server-Sent Events och andra förbättringar<a href="https://trpc.io/sv/blog/announcing-trpc-v11#prenumerationer-server-sent-events-och-andra-f%C3%B6rb%C3%A4ttringar" class="hash-link" aria-label="Direktlänk till Prenumerationer: Server-Sent Events och andra förbättringar" title="Direktlänk till Prenumerationer: Server-Sent Events och andra förbättringar">​</a></h3>
<ul>
<li>
<p>tRPC v11 introducerar ett nytt sätt att hantera prenumerationer med <a href="https://trpc.io/sv/docs/server/subscriptions#websockets-or-server-sent-events">Server-Sent Events (SSE)</a>. Detta är ett utmärkt sätt att hantera realtidsuppdateringar i din applikation utan behov av mer komplexa WebSockets. Vi rekommenderar att använda detta som första val framöver.</p>
</li>
<li>
<p>Vi har lagt till stöd för JavaScript- <a href="https://trpc.io/sv/docs/server/subscriptions#cleanup-of-side-effects">generatorer i prenumerationer</a>. Detta gör att du kan skriva mer komplexa prenumerationshanterare som kan leverera flera värden över tid och städa upp efter sig på ett mycket JS-nativt sätt.</p>
</li>
<li>
<p>Prenumerationer stöder nu <a href="https://trpc.io/sv/docs/server/subscriptions#output-validation">utvalidering</a> vilket förbättrar typsäkerheten i dina prenumerationshanterare.</p>
</li>
</ul>
<h3 class="anchor anchorWithStickyNavbar_MYIC" id="adjö-v9-interop-läge">Adjö v9 <code>.interop()</code>-läge<a href="https://trpc.io/sv/blog/announcing-trpc-v11#adj%C3%B6-v9-interop-l%C3%A4ge" class="hash-link" aria-label="Direktlänk till adjö-v9-interop-läge" title="Direktlänk till adjö-v9-interop-läge">​</a></h3>
<p>I tRPC v10 introducerade vi <code>.interop()</code>-läget för att ge en smidig migreringsväg för tRPC v9-användare. Med tRPC v11 har vi tagit bort <code>.interop()</code>-läget. Om du fortfarande använder <code>.interop()</code>-läget kan du följa <a href="https://trpc.io/docs/v10/migrate-from-v9-to-v10" target="_blank" rel="noopener noreferrer">v10-migreringsguiden</a> för att slutföra din övergång till de API<!-- -->:er<!-- --> som används av dagens tRPC.</p>
<h2 class="anchor anchorWithStickyNavbar_MYIC" id="migrera-till-v11">Migrera till v11<a href="https://trpc.io/sv/blog/announcing-trpc-v11#migrera-till-v11" class="hash-link" aria-label="Direktlänk till Migrera till v11" title="Direktlänk till Migrera till v11">​</a></h2>
<p>Om du för närvarande använder tRPC v10 kan du följa <a href="https://trpc.io/sv/docs/migrate-from-v10-to-v11">migreringsguiden</a> för att uppgradera till v11. Guiden täcker alla brytande ändringar och nya funktioner i v11.</p>
<h2 class="anchor anchorWithStickyNavbar_MYIC" id="tack">Tack!<a href="https://trpc.io/sv/blog/announcing-trpc-v11#tack" class="hash-link" aria-label="Direktlänk till Tack!" title="Direktlänk till Tack!">​</a></h2>
<p>Från hela tRPC Core Team - tack för att du använder och stöder tRPC.</p>
<hr>
<h2 class="anchor anchorWithStickyNavbar_MYIC" id="slug-typescript-performance-lessonstitle-lärdomar-om-typescript-prestanda-vid-omstrukturering-för-v10authors-sachinraja">slug: typescript-performance-lessons
title: Lärdomar om TypeScript-prestanda vid omstrukturering för v10
authors: [sachinraja]<a href="https://trpc.io/sv/blog/announcing-trpc-v11#slug-typescript-performance-lessonstitle-l%C3%A4rdomar-om-typescript-prestanda-vid-omstrukturering-f%C3%B6r-v10authors-sachinraja" class="hash-link" aria-label="Direktlänk till slug: typescript-performance-lessons
title: Lärdomar om TypeScript-prestanda vid omstrukturering för v10
authors: [sachinraja]" title="Direktlänk till slug: typescript-performance-lessons
title: Lärdomar om TypeScript-prestanda vid omstrukturering för v10
authors: [sachinraja]">​</a></h2>
<ul>
<li>
<p>Följ <a href="https://twitter.com/trpcio" target="_blank" rel="noopener noreferrer">@trpcio</a> på Twitter.</p>
</li>
<li>
<p>Gå med i vår <a href="https://trpc.io/discord" target="_blank" rel="noopener noreferrer">Discord-community</a></p>
</li>
<li>
<p><a href="https://trpc.io/#try-it-out" target="_blank" rel="noopener noreferrer">Testa tRPC direkt i webbläsaren</a></p>
</li>
</ul>
<a id="sponsor-button" href="https://trpc.io/sponsor" class="group flex h-12 w-max items-center gap-4 rounded-lg border-2 bg-zinc-200 px-4 py-2 transition hover:bg-zinc-100 dark:border-zinc-900 dark:bg-zinc-800 hover:dark:border-zinc-700 hover:dark:bg-zinc-900"><svg role="img" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg" class="aspect-square h-6 fill-pink-500 transition-transform duration-200 ease-in group-hover:scale-110"><path d="M17.625 1.499c-2.32 0-4.354 1.203-5.625 3.03-1.271-1.827-3.305-3.03-5.625-3.03C3.129 1.499 0 4.253 0 8.249c0 4.275 3.068 7.847 5.828 10.227a33.14 33.14 0 0 0 5.616 3.876l.028.017.008.003-.001.003c.163.085.342.126.521.125.179.001.358-.041.521-.125l-.001-.003.008-.003.028-.017a33.14 33.14 0 0 0 5.616-3.876C20.932 16.096 24 12.524 24 8.249c0-3.996-3.129-6.75-6.375-6.75zm-.919 15.275a30.766 30.766 0 0 1-4.703 3.316l-.004-.002-.004.002a30.955 30.955 0 0 1-4.703-3.316c-2.677-2.307-5.047-5.298-5.047-8.523 0-2.754 2.121-4.5 4.125-4.5 2.06 0 3.914 1.479 4.544 3.684.143.495.596.797 1.086.796.49.001.943-.302 1.085-.796.63-2.205 2.484-3.684 4.544-3.684 2.004 0 4.125 1.746 4.125 4.5 0 3.225-2.37 6.216-5.048 8.523z"></path></svg><span class="font-semibold text-zinc-900 no-underline dark:text-zinc-300">Bli sponsor!</span></a>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Introduktion av den nya TanStack React Query-integrationen]]></title>
            <link>https://trpc.io/sv/blog/introducing-tanstack-react-query-client</link>
            <guid>https://trpc.io/sv/blog/introducing-tanstack-react-query-client</guid>
            <pubDate>Mon, 17 Feb 2025 00:00:00 GMT</pubDate>
            <description><![CDATA[Denna sida har översatts av PageTurner AI (beta). Inte officiellt godkänd av projektet.]]></description>
            <content:encoded><![CDATA[<div class="theme-admonition theme-admonition-info admonition_v5Ag alert alert--info"><div class="admonitionHeading_usrK"><span class="admonitionIcon_bgEp"><svg viewBox="0 0 14 16"><path fill-rule="evenodd" d="M7 2.3c3.14 0 5.7 2.56 5.7 5.7s-2.56 5.7-5.7 5.7A5.71 5.71 0 0 1 1.3 8c0-3.14 2.56-5.7 5.7-5.7zM7 1C3.14 1 0 4.14 0 8s3.14 7 7 7 7-3.14 7-7-3.14-7-7-7zm1 3H6v5h2V4zm0 6H6v2h2v-2z"></path></svg></span>Inofficiell Beta-översättning</div><div class="admonitionContent_e2NW"><p>Denna sida har översatts av <a href="https://page-turner.com/" target="_blank" rel="noopener noreferrer"><strong>PageTurner</strong></a> AI (beta). Inte officiellt godkänd av projektet.
Hittade du ett fel? <a href="https://feedback.page-turner.com/?repo_id=683d130a-1828-4b22-91cd-ef2d269ef3f5&amp;file_path=blog%2F2025-02-17-new-tanstack-react-query-integration.mdx&amp;locale=sv" target="_blank" rel="noopener noreferrer">Rapportera problem →</a></p></div></div>
<p>Vi är glada att meddela att den nya TanStack React Query-integrationen för tRPC nu finns tillgänglig i tRPC<!-- -->:s<!-- --> <code>next</code>-release. Jämfört med vår klassiska <a href="https://trpc.io/sv/docs/client/react">React Query-integration</a> är den enklare och mer TanStack Query-inriktad genom att direkt använda de inbyggda gränssnitten <a href="https://tanstack.com/query/latest/docs/framework/react/guides/query-options" target="_blank" rel="noopener noreferrer">QueryOptions</a> och <a href="https://tanstack.com/query/latest/docs/framework/react/guides/mutations" target="_blank" rel="noopener noreferrer">MutationOptions</a> från TanStack React Query, istället för att wrappa <code>useQuery</code> och <code>useMutation</code> med vår egen klient.</p>
<!-- -->
<div><pre class="shiki min-light with-title twoslash lsp" style="background-color: #ffffff; color: #24292eff" title="greeting.tsx"><div class="code-title">greeting.tsx</div><div class="language-id">tsx</div><div class="code-container"><code><div class="line"><span style="color: #D32F2F">import</span><span style="color: #24292EFF"> { <data-lsp lsp="(alias) function useQuery<TQueryFnData = unknown, TError = Error, TData = TQueryFnData, TQueryKey extends QueryKey = readonly unknown[]>(options: DefinedInitialDataOptions<TQueryFnData, TError, TData, TQueryKey>, queryClient?: QueryClient): DefinedUseQueryResult<NoInfer<TData>, TError> (+2 overloads)
import useQuery">useQuery</data-lsp> } </span><span style="color: #D32F2F">from</span><span style="color: #24292EFF"> </span><span style="color: #22863A">'@tanstack/react-query'</span><span style="color: #24292EFF">;</span></div><div class="line"><span style="color: #D32F2F">import</span><span style="color: #24292EFF"> { <data-lsp lsp="(alias) const useTRPC: () => TRPCOptionsProxy<BuiltRouter<{
    ctx: object;
    meta: object;
    errorShape: DefaultErrorShape;
    transformer: false;
}, DecorateCreateRouterOptions<{
    greeting: QueryProcedure<{
        input: {
            name: string;
        };
        output: string;
        meta: object;
    }>;
}>>, {
    keyPrefix: false;
}>
import useTRPC">useTRPC</data-lsp> } </span><span style="color: #D32F2F">from</span><span style="color: #24292EFF"> </span><span style="color: #22863A">'./trpc'</span><span style="color: #24292EFF">;</span></div><div class="line">&nbsp;</div><div class="line"><span style="color: #D32F2F">export</span><span style="color: #24292EFF"> </span><span style="color: #D32F2F">function</span><span style="color: #24292EFF"> </span><span style="color: #6F42C1"><data-lsp lsp="function Greeting(): null">Greeting</data-lsp></span><span style="color: #24292EFF">() {</span></div><div class="line"><span style="color: #24292EFF">  </span><span style="color: #D32F2F">const</span><span style="color: #24292EFF"> </span><span style="color: #1976D2"><data-lsp lsp="const trpc: TRPCOptionsProxy<BuiltRouter<{
    ctx: object;
    meta: object;
    errorShape: DefaultErrorShape;
    transformer: false;
}, DecorateCreateRouterOptions<{
    greeting: QueryProcedure<{
        input: {
            name: string;
        };
        output: string;
        meta: object;
    }>;
}>>, {
    keyPrefix: false;
}>">trpc</data-lsp></span><span style="color: #24292EFF"> </span><span style="color: #D32F2F">=</span><span style="color: #24292EFF"> </span><span style="color: #6F42C1"><data-lsp lsp="(alias) useTRPC(): TRPCOptionsProxy<BuiltRouter<{
    ctx: object;
    meta: object;
    errorShape: DefaultErrorShape;
    transformer: false;
}, DecorateCreateRouterOptions<{
    greeting: QueryProcedure<{
        input: {
            name: string;
        };
        output: string;
        meta: object;
    }>;
}>>, {
    keyPrefix: false;
}>
import useTRPC">useTRPC</data-lsp></span><span style="color: #24292EFF">();</span></div><div class="line">&nbsp;</div><div class="line"><span style="color: #24292EFF">  </span><span style="color: #D32F2F">const</span><span style="color: #24292EFF"> </span><span style="color: #1976D2"><data-lsp lsp="const greetingQuery: UseQueryResult<string, TRPCClientErrorLike<{
    transformer: false;
    errorShape: DefaultErrorShape;
}>>">greetingQuery</data-lsp></span><span style="color: #24292EFF"> </span><span style="color: #D32F2F">=</span><span style="color: #24292EFF"> </span><span style="color: #6F42C1"><data-lsp lsp="(alias) useQuery<string, TRPCClientErrorLike<{
    transformer: false;
    errorShape: DefaultErrorShape;
}>, string, TRPCQueryKeyWithoutPrefix>(options: UndefinedInitialDataOptions<string, TRPCClientErrorLike<{
    transformer: false;
    errorShape: DefaultErrorShape;
}>, string, TRPCQueryKeyWithoutPrefix>, queryClient?: QueryClient): UseQueryResult<string, TRPCClientErrorLike<{
    transformer: false;
    errorShape: DefaultErrorShape;
}>> (+2 overloads)
import useQuery">useQuery</data-lsp></span><span style="color: #24292EFF">(</span><span style="color: #1976D2"><data-lsp lsp="const trpc: TRPCOptionsProxy<BuiltRouter<{
    ctx: object;
    meta: object;
    errorShape: DefaultErrorShape;
    transformer: false;
}, DecorateCreateRouterOptions<{
    greeting: QueryProcedure<{
        input: {
            name: string;
        };
        output: string;
        meta: object;
    }>;
}>>, {
    keyPrefix: false;
}>">trpc</data-lsp></span><span style="color: #6F42C1">.</span><span style="color: #1976D2"><data-lsp lsp="(property) greeting: DecorateQueryProcedure<{
    input: {
        name: string;
    };
    output: string;
    transformer: false;
    errorShape: DefaultErrorShape;
    featureFlags: {
        keyPrefix: false;
    };
}> &amp; Record<string, never>">greeting</data-lsp></span><span style="color: #6F42C1">.<data-lsp lsp="(property) DecorateQueryProcedure<{ input: { name: string; }; output: string; transformer: false; errorShape: DefaultErrorShape; featureFlags: { keyPrefix: false; }; }>.queryOptions: TRPCQueryOptions
<string, string>(input: {
    name: string;
}, opts?: UnusedSkipTokenTRPCQueryOptionsIn<string, string, TRPCClientErrorLike<{
    transformer: false;
    errorShape: DefaultErrorShape;
}>, {
    keyPrefix: false;
}> | undefined) => UnusedSkipTokenTRPCQueryOptionsOut<string, string, TRPCClientErrorLike<{
    transformer: false;
    errorShape: DefaultErrorShape;
}>, {
    keyPrefix: false;
}> (+2 overloads)">queryOptions</data-lsp></span><span style="color: #24292EFF">({ <data-lsp lsp="(property) name: string">name</data-lsp></span><span style="color: #D32F2F">:</span><span style="color: #24292EFF"> </span><span style="color: #22863A">'Jerry'</span><span style="color: #24292EFF"> }));</span></div><div class="line">&nbsp;</div><div class="line"><span style="color: #24292EFF">  </span><span style="color: #C2C3C5">// greetingQuery.data === 'Hello Jerry'</span></div><div class="line">&nbsp;</div><div class="line"><span style="color: #24292EFF">  </span><span style="color: #D32F2F">return</span><span style="color: #24292EFF"> </span><span style="color: #1976D2">null</span><span style="color: #24292EFF">;</span></div><div class="line"><span style="color: #24292EFF">}</span></div></code></div><button type="button" aria-label="Copy code to clipboard" class="copy-button" onclick="navigator.clipboard.writeText(this.previousSibling.innerText),this.classList.add(&quot;copied&quot;),this.textContent=&quot;Copied&quot;,setTimeout(()=>{this.classList.remove(&quot;copied&quot;),this.textContent=&quot;Copy&quot;},2e3)">Copy</button></pre>
<pre class="shiki github-dark with-title twoslash lsp" style="background-color: #0d1117; color: #c9d1d9" title="greeting.tsx"><div class="code-title">greeting.tsx</div><div class="language-id">tsx</div><div class="code-container"><code><div class="line"><span style="color: #FF7B72">import</span><span style="color: #C9D1D9"> { <data-lsp lsp="(alias) function useQuery<TQueryFnData = unknown, TError = Error, TData = TQueryFnData, TQueryKey extends QueryKey = readonly unknown[]>(options: DefinedInitialDataOptions<TQueryFnData, TError, TData, TQueryKey>, queryClient?: QueryClient): DefinedUseQueryResult<NoInfer<TData>, TError> (+2 overloads)
import useQuery">useQuery</data-lsp> } </span><span style="color: #FF7B72">from</span><span style="color: #C9D1D9"> </span><span style="color: #A5D6FF">'@tanstack/react-query'</span><span style="color: #C9D1D9">;</span></div><div class="line"><span style="color: #FF7B72">import</span><span style="color: #C9D1D9"> { <data-lsp lsp="(alias) const useTRPC: () => TRPCOptionsProxy<BuiltRouter<{
    ctx: object;
    meta: object;
    errorShape: DefaultErrorShape;
    transformer: false;
}, DecorateCreateRouterOptions<{
    greeting: QueryProcedure<{
        input: {
            name: string;
        };
        output: string;
        meta: object;
    }>;
}>>, {
    keyPrefix: false;
}>
import useTRPC">useTRPC</data-lsp> } </span><span style="color: #FF7B72">from</span><span style="color: #C9D1D9"> </span><span style="color: #A5D6FF">'./trpc'</span><span style="color: #C9D1D9">;</span></div><div class="line">&nbsp;</div><div class="line"><span style="color: #FF7B72">export</span><span style="color: #C9D1D9"> </span><span style="color: #FF7B72">function</span><span style="color: #C9D1D9"> </span><span style="color: #D2A8FF"><data-lsp lsp="function Greeting(): null">Greeting</data-lsp></span><span style="color: #C9D1D9">() {</span></div><div class="line"><span style="color: #C9D1D9">  </span><span style="color: #FF7B72">const</span><span style="color: #C9D1D9"> </span><span style="color: #79C0FF"><data-lsp lsp="const trpc: TRPCOptionsProxy<BuiltRouter<{
    ctx: object;
    meta: object;
    errorShape: DefaultErrorShape;
    transformer: false;
}, DecorateCreateRouterOptions<{
    greeting: QueryProcedure<{
        input: {
            name: string;
        };
        output: string;
        meta: object;
    }>;
}>>, {
    keyPrefix: false;
}>">trpc</data-lsp></span><span style="color: #C9D1D9"> </span><span style="color: #FF7B72">=</span><span style="color: #C9D1D9"> </span><span style="color: #D2A8FF"><data-lsp lsp="(alias) useTRPC(): TRPCOptionsProxy<BuiltRouter<{
    ctx: object;
    meta: object;
    errorShape: DefaultErrorShape;
    transformer: false;
}, DecorateCreateRouterOptions<{
    greeting: QueryProcedure<{
        input: {
            name: string;
        };
        output: string;
        meta: object;
    }>;
}>>, {
    keyPrefix: false;
}>
import useTRPC">useTRPC</data-lsp></span><span style="color: #C9D1D9">();</span></div><div class="line">&nbsp;</div><div class="line"><span style="color: #C9D1D9">  </span><span style="color: #FF7B72">const</span><span style="color: #C9D1D9"> </span><span style="color: #79C0FF"><data-lsp lsp="const greetingQuery: UseQueryResult<string, TRPCClientErrorLike<{
    transformer: false;
    errorShape: DefaultErrorShape;
}>>">greetingQuery</data-lsp></span><span style="color: #C9D1D9"> </span><span style="color: #FF7B72">=</span><span style="color: #C9D1D9"> </span><span style="color: #D2A8FF"><data-lsp lsp="(alias) useQuery<string, TRPCClientErrorLike<{
    transformer: false;
    errorShape: DefaultErrorShape;
}>, string, TRPCQueryKeyWithoutPrefix>(options: UndefinedInitialDataOptions<string, TRPCClientErrorLike<{
    transformer: false;
    errorShape: DefaultErrorShape;
}>, string, TRPCQueryKeyWithoutPrefix>, queryClient?: QueryClient): UseQueryResult<string, TRPCClientErrorLike<{
    transformer: false;
    errorShape: DefaultErrorShape;
}>> (+2 overloads)
import useQuery">useQuery</data-lsp></span><span style="color: #C9D1D9">(<data-lsp lsp="const trpc: TRPCOptionsProxy<BuiltRouter<{
    ctx: object;
    meta: object;
    errorShape: DefaultErrorShape;
    transformer: false;
}, DecorateCreateRouterOptions<{
    greeting: QueryProcedure<{
        input: {
            name: string;
        };
        output: string;
        meta: object;
    }>;
}>>, {
    keyPrefix: false;
}>">trpc</data-lsp>.<data-lsp lsp="(property) greeting: DecorateQueryProcedure<{
    input: {
        name: string;
    };
    output: string;
    transformer: false;
    errorShape: DefaultErrorShape;
    featureFlags: {
        keyPrefix: false;
    };
}> &amp; Record<string, never>">greeting</data-lsp>.</span><span style="color: #D2A8FF"><data-lsp lsp="(property) DecorateQueryProcedure<{ input: { name: string; }; output: string; transformer: false; errorShape: DefaultErrorShape; featureFlags: { keyPrefix: false; }; }>.queryOptions: TRPCQueryOptions
<string, string>(input: {
    name: string;
}, opts?: UnusedSkipTokenTRPCQueryOptionsIn<string, string, TRPCClientErrorLike<{
    transformer: false;
    errorShape: DefaultErrorShape;
}>, {
    keyPrefix: false;
}> | undefined) => UnusedSkipTokenTRPCQueryOptionsOut<string, string, TRPCClientErrorLike<{
    transformer: false;
    errorShape: DefaultErrorShape;
}>, {
    keyPrefix: false;
}> (+2 overloads)">queryOptions</data-lsp></span><span style="color: #C9D1D9">({ <data-lsp lsp="(property) name: string">name</data-lsp>: </span><span style="color: #A5D6FF">'Jerry'</span><span style="color: #C9D1D9"> }));</span></div><div class="line">&nbsp;</div><div class="line"><span style="color: #C9D1D9">  </span><span style="color: #8B949E">// greetingQuery.data === 'Hello Jerry'</span></div><div class="line">&nbsp;</div><div class="line"><span style="color: #C9D1D9">  </span><span style="color: #FF7B72">return</span><span style="color: #C9D1D9"> </span><span style="color: #79C0FF">null</span><span style="color: #C9D1D9">;</span></div><div class="line"><span style="color: #C9D1D9">}</span></div></code></div><button type="button" aria-label="Copy code to clipboard" class="copy-button" onclick="navigator.clipboard.writeText(this.previousSibling.innerText),this.classList.add(&quot;copied&quot;),this.textContent=&quot;Copied&quot;,setTimeout(()=>{this.classList.remove(&quot;copied&quot;),this.textContent=&quot;Copy&quot;},2e3)">Copy</button></pre></div>
<p>Med denna nya klient tar vi bort ett abstraktionslager som ofta orsakar förvirring för nya användare, och erbjuder istället ett mer direkt sätt att arbeta med TanStack React Query som känns omedelbart bekant för de som följer TanStacks egna <a href="https://tanstack.com/query/latest" target="_blank" rel="noopener noreferrer">dokumentation</a>. Det innebär också att vi behöver mindre tRPC-dokumentation för att förklara den, även om vi naturligtvis har <a href="https://trpc.io/sv/docs/client/tanstack-react-query/setup">dokumentation för att komma igång</a>.</p>
<h2 class="anchor anchorWithStickyNavbar_MYIC" id="varför-denna-förändring">Varför denna förändring?<a href="https://trpc.io/sv/blog/introducing-tanstack-react-query-client#varf%C3%B6r-denna-f%C3%B6r%C3%A4ndring" class="hash-link" aria-label="Direktlänk till Varför denna förändring?" title="Direktlänk till Varför denna förändring?">​</a></h2>
<p>Du kan läsa vår ursprungliga RFC för denna förändring <a href="https://github.com/trpc/trpc/discussions/6240" target="_blank" rel="noopener noreferrer">här</a> som går in på detaljer. Men några av våra huvudsakliga skäl är:</p>
<ul>
<li>
<p><strong>Enkelhet</strong>: Den nya klienten är enklare och mer TanStack Query-inriktad genom att tillhandahålla fabriker för vanliga TanStack React Query-gränssnitt som QueryKeys, QueryOptions och MutationOptions. Detta minskar inlärningskurvan eftersom du kan följa den <a href="https://tanstack.com/query/latest" target="_blank" rel="noopener noreferrer">officiella TanStack Query-dokumentationen</a></p>
</li>
<li>
<p><strong>Bekanthet</strong>: Den nya klienten är mer bekant för de som redan använder TanStack Query, där du använder TanStack Query för andra arbetsbelastningar i din applikation utan att tvingas använda en alternativ syntax för tRPC</p>
</li>
<li>
<p><strong>React</strong>: Vår klassiska React Query-integration bryter faktiskt mot hooks-regler; den <a href="https://github.com/trpc/trpc/issues/2330" target="_blank" rel="noopener noreferrer">kan inte korrekt linteras</a> och uppmuntrar vissa mönster som kommer att brytas under React Compiler, som att skicka hooks som props. I detta avseende är den nya klienten mer idiomatisk React</p>
</li>
<li>
<p><strong>Underhållbarhet</strong>: En utmaning vi haft med vår versionshantering är att hålla tRPC i takt med förändringar i TanStack Query, särskilt nya funktioner som tillförs QueryClient då och då. Genom att använda det minimala gränssnittet för native interfaces kan vi stödja React Query mycket enklare, samtidigt som vi följer vad <a href="https://bsky.app/profile/tkdodo.eu/post/3lgizrcvjmc24" target="_blank" rel="noopener noreferrer">TanStacks maintainers anser vara bästa praxis</a></p>
</li>
<li>
<p><strong>Feedback</strong>: Som vi nämnt orsakar den klassiska klienten ofta svårigheter för nya användare, men feedbacken vi fick på <a href="https://github.com/trpc/trpc/discussions/6240" target="_blank" rel="noopener noreferrer">RFC<!-- -->:en</a> för denna klient var överväldigande positiv, där majoriteten av användare som lämnade kommentarer eller meddelanden var entusiastiska över att använda denna klient. Naturligtvis är inte alla övertygade om klienten än, så vi kommer att behålla den klassiska klienten</p>
</li>
</ul>
<h2 class="anchor anchorWithStickyNavbar_MYIC" id="vad-händer-med-den-klassiska-trpc-react-query-integrationen">Vad händer med den klassiska tRPC React Query-integrationen?<a href="https://trpc.io/sv/blog/introducing-tanstack-react-query-client#vad-h%C3%A4nder-med-den-klassiska-trpc-react-query-integrationen" class="hash-link" aria-label="Direktlänk till Vad händer med den klassiska tRPC React Query-integrationen?" title="Direktlänk till Vad händer med den klassiska tRPC React Query-integrationen?">​</a></h2>
<p>Den försvinner inte på länge! Vi åtar oss att underhålla den länge framöver, men den kommer inte att få några betydande nya funktioner och vi kommer att betrakta den som stabil.</p>
<p>Vi rekommenderar fortfarande att nya projekt startar med den nya TanStack React Query-integrationen, och befintliga projekt bör överväga att migrera gradvis.</p>
<h2 class="anchor anchorWithStickyNavbar_MYIC" id="hur-migrerar-jag">Hur migrerar jag?<a href="https://trpc.io/sv/blog/introducing-tanstack-react-query-client#hur-migrerar-jag" class="hash-link" aria-label="Direktlänk till Hur migrerar jag?" title="Direktlänk till Hur migrerar jag?">​</a></h2>
<p>Även om den klassiska klienten kommer att underhållas länge framöver rekommenderar vi att nya projekt startar med den nya klienten och aktiva projekt överväger en gradvis migrering.</p>
<p>Båda klienterna är kompatibla med varandra och kan finnas i samma applikation, så du kan migrera i din egen takt. Vi arbetar också med en codemod där <strong>vi skulle älska</strong> bidrag från communityn. Vi vill tacka <a href="https://bsky.app/profile/reaper.is" target="_blank" rel="noopener noreferrer">@reaper</a> för hans bidrag till codemoden hittills!</p>
<p>👉 <a href="https://trpc.io/sv/docs/client/tanstack-react-query/migrating">Läs migrationsdokumentationen</a></p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Använda Server Actions med tRPC]]></title>
            <link>https://trpc.io/sv/blog/trpc-actions</link>
            <guid>https://trpc.io/sv/blog/trpc-actions</guid>
            <pubDate>Thu, 23 May 2024 00:00:00 GMT</pubDate>
            <description><![CDATA[Denna sida har översatts av PageTurner AI (beta). Inte officiellt godkänd av projektet.]]></description>
            <content:encoded><![CDATA[<div class="theme-admonition theme-admonition-info admonition_v5Ag alert alert--info"><div class="admonitionHeading_usrK"><span class="admonitionIcon_bgEp"><svg viewBox="0 0 14 16"><path fill-rule="evenodd" d="M7 2.3c3.14 0 5.7 2.56 5.7 5.7s-2.56 5.7-5.7 5.7A5.71 5.71 0 0 1 1.3 8c0-3.14 2.56-5.7 5.7-5.7zM7 1C3.14 1 0 4.14 0 8s3.14 7 7 7 7-3.14 7-7-3.14-7-7-7zm1 3H6v5h2V4zm0 6H6v2h2v-2z"></path></svg></span>Inofficiell Beta-översättning</div><div class="admonitionContent_e2NW"><p>Denna sida har översatts av <a href="https://page-turner.com/" target="_blank" rel="noopener noreferrer"><strong>PageTurner</strong></a> AI (beta). Inte officiellt godkänd av projektet.
Hittade du ett fel? <a href="https://feedback.page-turner.com/?repo_id=683d130a-1828-4b22-91cd-ef2d269ef3f5&amp;file_path=blog%2F2024-05-23-trpc-actions.mdx&amp;locale=sv" target="_blank" rel="noopener noreferrer">Rapportera problem →</a></p></div></div>
<!-- -->
<p>Byggarmönstret för att skapa procedurer som introducerades i tRPC v10 har fått stor uppskattning från communityt, och många bibliotek har antagit liknande mönster.
Det har till och med myntats en term <code>tRPC like XYZ</code> som bevis på mönstrets ökande popularitet. För inte så länge sedan såg jag faktiskt <a href="https://x.com/localhost_5173/status/1793259910723215835" target="_blank" rel="noopener noreferrer">någon
som undrade om det fanns ett sätt att skriva CLI-applikationer med ett liknande API som tRPC</a>.
Sidnotering: du kan till och med använda <a href="https://github.com/mmkal/trpc-cli" target="_blank" rel="noopener noreferrer">tRPC direkt för detta</a>. Men det är inte det vi ska prata om idag,
vi ska diskutera hur man använder tRPC med server actions från Next.js.</p>
<!-- -->
<h2 class="anchor anchorWithStickyNavbar_MYIC" id="vad-är-en-server-action">Vad är en server action?<a href="https://trpc.io/sv/blog/trpc-actions#vad-%C3%A4r-en-server-action" class="hash-link" aria-label="Direktlänk till Vad är en server action?" title="Direktlänk till Vad är en server action?">​</a></h2>
<p>Om du levt under en sten och inte hängt med i de senaste <a href="https://react.dev/reference/rsc/server-actions" target="_blank" rel="noopener noreferrer">React</a>- och <a href="https://nextjs.org/docs/app/building-your-application/data-fetching/server-actions-and-mutations" target="_blank" rel="noopener noreferrer">Next.js</a>-funktionerna: server actions låter dig skriva vanliga funktioner som körs på servern, importera dem på klienten och anropa dem precis som om de vore vanliga funktioner.
Du kanske tycker att detta låter likt tRPC - och det stämmer. Enligt <a href="https://x.com/dan_abramov2" target="_blank" rel="noopener noreferrer">Dan Abramov</a> är server actions "tRPC som en bundler-funktion":</p>
<div class="mb-4 flex w-full justify-center"><div class="react-tweet-theme root_gzUe root_OzMz"><article class="article_zVA9"><span class="skeleton_XJfA" style="height:3rem;margin-bottom:0.75rem"></span><span class="skeleton_XJfA" style="height:6rem;margin:0.5rem 0"></span><div style="border-top:var(--tweet-border);margin:0.5rem 0"></div><span class="skeleton_XJfA" style="height:2rem"></span><span class="skeleton_XJfA" style="height:2rem;border-radius:9999px;margin-top:0.5rem"></span></article></div></div>
<p>Och detta är helt korrekt. Server actions liknar tRPC, i slutändan är de båda <a href="https://en.wikipedia.org/wiki/Remote_procedure_call" target="_blank" rel="noopener noreferrer">RPC<!-- -->:er</a>. Båda låter dig skriva funktioner på backend och anropa dem med full typstyrning på frontend med nätverkslagret abstraherat bort.</p>
<p>Så var kommer tRPC in i bilden? Varför skulle jag behöva både tRPC och server actions? Server actions är en primitiv, och som alla primitiver är de ganska grundläggande och saknar därmed vissa fundamentala aspekter när det gäller att bygga API<!-- -->:er<!-- -->. För alla API-slutpunkter som exponeras över nätverket måste du validera och auktorisera förfrågningar för att säkerställa att ditt API inte missbrukas. Som tidigare nämnts uppskattas tRPC<!-- -->:s<!-- --> API av communityt, så vore det inte trevligt om vi kunde använda tRPC för att definiera server actions och utnyttja alla fantastiska funktioner som kommer inbyggda i tRPC? Tänk validering av indata, autentisering och auktorisering via middleware, validering av utdata, datatransformatorer, etc. Jag tycker det, så låt oss gräva djupare.</p>
<h2 class="anchor anchorWithStickyNavbar_MYIC" id="definiera-server-actions-med-trpc">Definiera server actions med tRPC<a href="https://trpc.io/sv/blog/trpc-actions#definiera-server-actions-med-trpc" class="hash-link" aria-label="Direktlänk till Definiera server actions med tRPC" title="Direktlänk till Definiera server actions med tRPC">​</a></h2>
<div class="theme-admonition theme-admonition-note admonition_v5Ag alert alert--secondary"><div class="admonitionHeading_usrK"><span class="admonitionIcon_bgEp"><svg viewBox="0 0 14 16"><path fill-rule="evenodd" d="M6.3 5.69a.942.942 0 0 1-.28-.7c0-.28.09-.52.28-.7.19-.18.42-.28.7-.28.28 0 .52.09.7.28.18.19.28.42.28.7 0 .28-.09.52-.28.7a1 1 0 0 1-.7.3c-.28 0-.52-.11-.7-.3zM8 7.99c-.02-.25-.11-.48-.31-.69-.2-.19-.42-.3-.69-.31H6c-.27.02-.48.13-.69.31-.2.2-.3.44-.31.69h1v3c.02.27.11.5.31.69.2.2.42.31.69.31h1c.27 0 .48-.11.69-.31.2-.19.3-.42.31-.69H8V7.98v.01zM7 2.3c-3.14 0-5.7 2.54-5.7 5.68 0 3.14 2.56 5.7 5.7 5.7s5.7-2.55 5.7-5.7c0-3.15-2.56-5.69-5.7-5.69v.01zM7 .98c3.86 0 7 3.14 7 7s-3.14 7-7 7-7-3.12-7-7 3.14-7 7-7z"></path></svg></span>notering</div><div class="admonitionContent_e2NW"><p><strong>Förutsättningar:</strong> För att använda server actions måste du använda Next.js App Router. Dessutom är alla tRPC-funktioner vi ska använda endast tillgängliga i tRPC v11, så se till att du använder beta-releasekanalen för tRPC:</p><div class="tabs-container tabList_mG0D"><ul role="tablist" aria-orientation="horizontal" class="tabs"><li role="tab" tabindex="0" aria-selected="true" class="tabs__item tabItem_F3SZ tabs__item--active">npm</li><li role="tab" tabindex="-1" aria-selected="false" class="tabs__item tabItem_F3SZ">yarn</li><li role="tab" tabindex="-1" aria-selected="false" class="tabs__item tabItem_F3SZ">pnpm</li><li role="tab" tabindex="-1" aria-selected="false" class="tabs__item tabItem_F3SZ">bun</li><li role="tab" tabindex="-1" aria-selected="false" class="tabs__item tabItem_F3SZ">deno</li></ul><div class="margin-top--md"><div role="tabpanel" class="tabItem_f_Dr"><pre language="bash" title="shell" class="InstallationSnippet__CodeBlock">npm install @trpc/server<button type="button" aria-label="Kopiera kod till urklipp" class="copy-button">Kopiera</button></pre></div><div role="tabpanel" class="tabItem_f_Dr" hidden=""><pre language="bash" title="shell" class="InstallationSnippet__CodeBlock">yarn add @trpc/server<button type="button" aria-label="Kopiera kod till urklipp" class="copy-button">Kopiera</button></pre></div><div role="tabpanel" class="tabItem_f_Dr" hidden=""><pre language="bash" title="shell" class="InstallationSnippet__CodeBlock">pnpm add @trpc/server<button type="button" aria-label="Kopiera kod till urklipp" class="copy-button">Kopiera</button></pre></div><div role="tabpanel" class="tabItem_f_Dr" hidden=""><pre language="bash" title="shell" class="InstallationSnippet__CodeBlock">bun add @trpc/server<button type="button" aria-label="Kopiera kod till urklipp" class="copy-button">Kopiera</button></pre></div><div role="tabpanel" class="tabItem_f_Dr" hidden=""><pre language="bash" title="shell" class="InstallationSnippet__CodeBlock">deno add npm:@trpc/server<button type="button" aria-label="Kopiera kod till urklipp" class="copy-button">Kopiera</button></pre></div></div></div></div></div>
<p>Låt oss börja med att initiera tRPC och definiera vår basprocedur för server actions.
Vi kommer använda metoden <code>experimental_caller</code> i procedure-builder<!-- -->:en<!-- -->, en ny metod som låter dig
anpassa hur proceduren anropas när den invokeras som en funktion. Vi kommer också använda adapter<!-- -->:en<!-- --> <code>experimental_nextAppDirCaller</code>
för att göra den kompatibel med Next.js. Denna adapter hanterar fall där server action<!-- -->:en<!-- --> är inlindad i <code>useActionState</code> på klienten,
vilket <a href="https://react.dev/reference/react/useActionState#my-action-can-no-longer-read-the-submitted-form-data" target="_blank" rel="noopener noreferrer">ändrar anropssignaturen för server action<!-- -->:en</a>.</p>
<p>Vi kommer också använda en <code>span</code>-egenskap som <a href="https://trpc.io/sv/docs/server/metadata">metadata</a>, eftersom det inte finns någon vanlig sökväg som när du använder en router (t.ex. <code>user.byId</code>). Du kan använda span-egenskapen för att skilja mellan procedurer, till exempel vid loggning eller observabilitet.</p>
<div><pre class="shiki min-light with-title twoslash lsp" style="background-color: #ffffff; color: #24292eff" title="server/trpc.ts"><div class="code-title">server/trpc.ts</div><div class="language-id">ts</div><div class="code-container"><code><div class="line"><span style="color: #D32F2F">import</span><span style="color: #24292EFF"> { <data-lsp lsp="(alias) const initTRPC: TRPCBuilder<object, object>
import initTRPC">initTRPC</data-lsp></span><span style="color: #212121">,</span><span style="color: #24292EFF"> <data-lsp lsp="(alias) class TRPCError
import TRPCError">TRPCError</data-lsp> } </span><span style="color: #D32F2F">from</span><span style="color: #24292EFF"> </span><span style="color: #22863A">'@trpc/server'</span><span style="color: #24292EFF">;</span></div><div class="line"><span style="color: #D32F2F">import</span><span style="color: #24292EFF"> { <data-lsp lsp="(alias) function experimental_nextAppDirCaller<TContext, TMeta>(config: Simplify<{
    pathExtractor?: (opts: {
        meta: TMeta;
    }) => string;
    normalizeFormData?: boolean;
    onError?: (opts: ErrorHandlerOptions<TContext>) => void;
} &amp; CreateContextCallback<TContext, () => MaybePromise<TContext>>>): CallerOverride<TContext>
import experimental_nextAppDirCaller">experimental_nextAppDirCaller</data-lsp> } </span><span style="color: #D32F2F">from</span><span style="color: #24292EFF"> </span><span style="color: #22863A">'@trpc/server/adapters/next-app-dir'</span><span style="color: #24292EFF">;</span></div><div class="line">&nbsp;</div><div class="line"><span style="color: #D32F2F">interface</span><span style="color: #24292EFF"> </span><span style="color: #6F42C1"><data-lsp lsp="interface Meta">Meta</data-lsp></span><span style="color: #24292EFF"> {</span></div><div class="line"><span style="color: #24292EFF">  <data-lsp lsp="(property) Meta.span: string">span</data-lsp></span><span style="color: #D32F2F">:</span><span style="color: #24292EFF"> </span><span style="color: #1976D2">string</span><span style="color: #24292EFF">;</span></div><div class="line"><span style="color: #24292EFF">}</span></div><div class="line">&nbsp;</div><div class="line"><span style="color: #D32F2F">export</span><span style="color: #24292EFF"> </span><span style="color: #D32F2F">const</span><span style="color: #24292EFF"> </span><span style="color: #1976D2"><data-lsp lsp="const t: TRPCRootObject<object, Meta, RuntimeConfigOptions<object, Meta>, {
    ctx: object;
    meta: Meta;
    errorShape: DefaultErrorShape;
    transformer: false;
}>">t</data-lsp></span><span style="color: #24292EFF"> </span><span style="color: #D32F2F">=</span><span style="color: #24292EFF"> </span><span style="color: #1976D2"><data-lsp lsp="(alias) const initTRPC: TRPCBuilder<object, object>
import initTRPC">initTRPC</data-lsp></span><span style="color: #6F42C1">.<data-lsp lsp="(method) TRPCBuilder<object, object>.meta<Meta>(): TRPCBuilder<object, Meta>">meta</data-lsp></span><span style="color: #24292EFF">&lt;</span><span style="color: #6F42C1"><data-lsp lsp="interface Meta">Meta</data-lsp></span><span style="color: #24292EFF">&gt;()</span><span style="color: #6F42C1">.<data-lsp lsp="(method) TRPCBuilder<object, Meta>.create<RuntimeConfigOptions<object, Meta>>(opts?: RuntimeConfigOptions<object, Meta> | undefined): TRPCRootObject<object, Meta, RuntimeConfigOptions<object, Meta>, {
    ctx: object;
    meta: Meta;
    errorShape: DefaultErrorShape;
    transformer: false;
}>">create</data-lsp></span><span style="color: #24292EFF">();</span></div><div class="line">&nbsp;</div><div class="line"><span style="color: #D32F2F">const</span><span style="color: #24292EFF"> </span><span style="color: #1976D2"><data-lsp lsp="const serverActionProcedure: ProcedureBuilder<object, Meta, object, UnsetMarker, UnsetMarker, UnsetMarker, UnsetMarker, true>">serverActionProcedure</data-lsp></span><span style="color: #24292EFF"> </span><span style="color: #D32F2F">=</span><span style="color: #24292EFF"> </span><span style="color: #1976D2"><data-lsp lsp="const t: TRPCRootObject<object, Meta, RuntimeConfigOptions<object, Meta>, {
    ctx: object;
    meta: Meta;
    errorShape: DefaultErrorShape;
    transformer: false;
}>">t</data-lsp></span><span style="color: #6F42C1">.</span><span style="color: #1976D2"><data-lsp lsp="(property) TRPCRootObject<object, Meta, RuntimeConfigOptions<object, Meta>, { ctx: object; meta: Meta; errorShape: DefaultErrorShape; transformer: false; }>.procedure: ProcedureBuilder<object, Meta, object, UnsetMarker, UnsetMarker, UnsetMarker, UnsetMarker, false>">procedure</data-lsp></span><span style="color: #6F42C1">.<data-lsp lsp="(method) ProcedureBuilder<object, Meta, object, UnsetMarker, UnsetMarker, UnsetMarker, UnsetMarker, false>.experimental_caller(caller: CallerOverride<object>): ProcedureBuilder<object, Meta, object, UnsetMarker, UnsetMarker, UnsetMarker, UnsetMarker, true>">experimental_caller</data-lsp></span><span style="color: #24292EFF">(</span></div><div class="line"><span style="color: #24292EFF">  </span><span style="color: #6F42C1"><data-lsp lsp="(alias) experimental_nextAppDirCaller<object, unknown>(config: {
    pathExtractor?: ((opts: {
        meta: unknown;
    }) => string) | undefined;
    normalizeFormData?: boolean | undefined;
    onError?: ((opts: ErrorHandlerOptions<object>) => void) | undefined;
    createContext?: (() => MaybePromise<object>) | undefined;
}): CallerOverride<object>
import experimental_nextAppDirCaller">experimental_nextAppDirCaller</data-lsp></span><span style="color: #24292EFF">({</span></div><div class="line"><span style="color: #24292EFF">    </span><span style="color: #6F42C1"><data-lsp lsp="(property) pathExtractor?: ((opts: {
    meta: unknown;
}) => string) | undefined">pathExtractor</data-lsp></span><span style="color: #D32F2F">:</span><span style="color: #24292EFF"> ({ <data-lsp lsp="(parameter) meta: unknown">meta</data-lsp> }) </span><span style="color: #D32F2F">=&gt;</span><span style="color: #24292EFF"> (<data-lsp lsp="(parameter) meta: unknown">meta</data-lsp> </span><span style="color: #D32F2F">as</span><span style="color: #24292EFF"> </span><span style="color: #6F42C1"><data-lsp lsp="interface Meta">Meta</data-lsp></span><span style="color: #24292EFF">).<data-lsp lsp="(property) Meta.span: string">span</data-lsp></span><span style="color: #212121">,</span></div><div class="line"><span style="color: #24292EFF">  })</span><span style="color: #212121">,</span></div><div class="line"><span style="color: #24292EFF">);</span></div></code></div><button type="button" aria-label="Copy code to clipboard" class="copy-button" onclick="navigator.clipboard.writeText(this.previousSibling.innerText),this.classList.add(&quot;copied&quot;),this.textContent=&quot;Copied&quot;,setTimeout(()=>{this.classList.remove(&quot;copied&quot;),this.textContent=&quot;Copy&quot;},2e3)">Copy</button></pre>
<pre class="shiki github-dark with-title twoslash lsp" style="background-color: #0d1117; color: #c9d1d9" title="server/trpc.ts"><div class="code-title">server/trpc.ts</div><div class="language-id">ts</div><div class="code-container"><code><div class="line"><span style="color: #FF7B72">import</span><span style="color: #C9D1D9"> { <data-lsp lsp="(alias) const initTRPC: TRPCBuilder<object, object>
import initTRPC">initTRPC</data-lsp>, <data-lsp lsp="(alias) class TRPCError
import TRPCError">TRPCError</data-lsp> } </span><span style="color: #FF7B72">from</span><span style="color: #C9D1D9"> </span><span style="color: #A5D6FF">'@trpc/server'</span><span style="color: #C9D1D9">;</span></div><div class="line"><span style="color: #FF7B72">import</span><span style="color: #C9D1D9"> { <data-lsp lsp="(alias) function experimental_nextAppDirCaller<TContext, TMeta>(config: Simplify<{
    pathExtractor?: (opts: {
        meta: TMeta;
    }) => string;
    normalizeFormData?: boolean;
    onError?: (opts: ErrorHandlerOptions<TContext>) => void;
} &amp; CreateContextCallback<TContext, () => MaybePromise<TContext>>>): CallerOverride<TContext>
import experimental_nextAppDirCaller">experimental_nextAppDirCaller</data-lsp> } </span><span style="color: #FF7B72">from</span><span style="color: #C9D1D9"> </span><span style="color: #A5D6FF">'@trpc/server/adapters/next-app-dir'</span><span style="color: #C9D1D9">;</span></div><div class="line">&nbsp;</div><div class="line"><span style="color: #FF7B72">interface</span><span style="color: #C9D1D9"> </span><span style="color: #FFA657"><data-lsp lsp="interface Meta">Meta</data-lsp></span><span style="color: #C9D1D9"> {</span></div><div class="line"><span style="color: #C9D1D9">  </span><span style="color: #FFA657"><data-lsp lsp="(property) Meta.span: string">span</data-lsp></span><span style="color: #FF7B72">:</span><span style="color: #C9D1D9"> </span><span style="color: #79C0FF">string</span><span style="color: #C9D1D9">;</span></div><div class="line"><span style="color: #C9D1D9">}</span></div><div class="line">&nbsp;</div><div class="line"><span style="color: #FF7B72">export</span><span style="color: #C9D1D9"> </span><span style="color: #FF7B72">const</span><span style="color: #C9D1D9"> </span><span style="color: #79C0FF"><data-lsp lsp="const t: TRPCRootObject<object, Meta, RuntimeConfigOptions<object, Meta>, {
    ctx: object;
    meta: Meta;
    errorShape: DefaultErrorShape;
    transformer: false;
}>">t</data-lsp></span><span style="color: #C9D1D9"> </span><span style="color: #FF7B72">=</span><span style="color: #C9D1D9"> <data-lsp lsp="(alias) const initTRPC: TRPCBuilder<object, object>
import initTRPC">initTRPC</data-lsp>.</span><span style="color: #D2A8FF"><data-lsp lsp="(method) TRPCBuilder<object, object>.meta<Meta>(): TRPCBuilder<object, Meta>">meta</data-lsp></span><span style="color: #C9D1D9">&lt;</span><span style="color: #FFA657"><data-lsp lsp="interface Meta">Meta</data-lsp></span><span style="color: #C9D1D9">&gt;().</span><span style="color: #D2A8FF"><data-lsp lsp="(method) TRPCBuilder<object, Meta>.create<RuntimeConfigOptions<object, Meta>>(opts?: RuntimeConfigOptions<object, Meta> | undefined): TRPCRootObject<object, Meta, RuntimeConfigOptions<object, Meta>, {
    ctx: object;
    meta: Meta;
    errorShape: DefaultErrorShape;
    transformer: false;
}>">create</data-lsp></span><span style="color: #C9D1D9">();</span></div><div class="line">&nbsp;</div><div class="line"><span style="color: #FF7B72">const</span><span style="color: #C9D1D9"> </span><span style="color: #79C0FF"><data-lsp lsp="const serverActionProcedure: ProcedureBuilder<object, Meta, object, UnsetMarker, UnsetMarker, UnsetMarker, UnsetMarker, true>">serverActionProcedure</data-lsp></span><span style="color: #C9D1D9"> </span><span style="color: #FF7B72">=</span><span style="color: #C9D1D9"> <data-lsp lsp="const t: TRPCRootObject<object, Meta, RuntimeConfigOptions<object, Meta>, {
    ctx: object;
    meta: Meta;
    errorShape: DefaultErrorShape;
    transformer: false;
}>">t</data-lsp>.<data-lsp lsp="(property) TRPCRootObject<object, Meta, RuntimeConfigOptions<object, Meta>, { ctx: object; meta: Meta; errorShape: DefaultErrorShape; transformer: false; }>.procedure: ProcedureBuilder<object, Meta, object, UnsetMarker, UnsetMarker, UnsetMarker, UnsetMarker, false>">procedure</data-lsp>.</span><span style="color: #D2A8FF"><data-lsp lsp="(method) ProcedureBuilder<object, Meta, object, UnsetMarker, UnsetMarker, UnsetMarker, UnsetMarker, false>.experimental_caller(caller: CallerOverride<object>): ProcedureBuilder<object, Meta, object, UnsetMarker, UnsetMarker, UnsetMarker, UnsetMarker, true>">experimental_caller</data-lsp></span><span style="color: #C9D1D9">(</span></div><div class="line"><span style="color: #C9D1D9">  </span><span style="color: #D2A8FF"><data-lsp lsp="(alias) experimental_nextAppDirCaller<object, unknown>(config: {
    pathExtractor?: ((opts: {
        meta: unknown;
    }) => string) | undefined;
    normalizeFormData?: boolean | undefined;
    onError?: ((opts: ErrorHandlerOptions<object>) => void) | undefined;
    createContext?: (() => MaybePromise<object>) | undefined;
}): CallerOverride<object>
import experimental_nextAppDirCaller">experimental_nextAppDirCaller</data-lsp></span><span style="color: #C9D1D9">({</span></div><div class="line"><span style="color: #C9D1D9">    </span><span style="color: #D2A8FF"><data-lsp lsp="(property) pathExtractor?: ((opts: {
    meta: unknown;
}) => string) | undefined">pathExtractor</data-lsp></span><span style="color: #C9D1D9">: ({ </span><span style="color: #FFA657"><data-lsp lsp="(parameter) meta: unknown">meta</data-lsp></span><span style="color: #C9D1D9"> }) </span><span style="color: #FF7B72">=&gt;</span><span style="color: #C9D1D9"> (<data-lsp lsp="(parameter) meta: unknown">meta</data-lsp> </span><span style="color: #FF7B72">as</span><span style="color: #C9D1D9"> </span><span style="color: #FFA657"><data-lsp lsp="interface Meta">Meta</data-lsp></span><span style="color: #C9D1D9">).<data-lsp lsp="(property) Meta.span: string">span</data-lsp>,</span></div><div class="line"><span style="color: #C9D1D9">  }),</span></div><div class="line"><span style="color: #C9D1D9">);</span></div></code></div><button type="button" aria-label="Copy code to clipboard" class="copy-button" onclick="navigator.clipboard.writeText(this.previousSibling.innerText),this.classList.add(&quot;copied&quot;),this.textContent=&quot;Copied&quot;,setTimeout(()=>{this.classList.remove(&quot;copied&quot;),this.textContent=&quot;Copy&quot;},2e3)">Copy</button></pre></div>
<p>Därefter lägger vi till <a href="https://trpc.io/sv/docs/server/context">kontext</a>. Eftersom vi inte kommer hosta en router med en vanlig HTTP-adapter kommer vi inte få någon kontext injicerad genom <code>createContext</code>-metoden på adapter<!-- -->:en<!-- -->. Istället använder vi en middleware för att injicera vår kontext. I detta exempel hämtar vi den aktuella användaren från sessionen och injicerar den i kontexten.</p>
<div></div>
<div><pre class="shiki min-light with-title twoslash lsp" style="background-color: #ffffff; color: #24292eff" title="server/trpc.ts"><div class="code-title">server/trpc.ts</div><div class="language-id">ts</div><div class="code-container"><code><div class="line"><span style="color: #D32F2F">import</span><span style="color: #24292EFF"> { <data-lsp lsp="(alias) const initTRPC: TRPCBuilder<object, object>
import initTRPC">initTRPC</data-lsp></span><span style="color: #212121">,</span><span style="color: #24292EFF"> <data-lsp lsp="(alias) class TRPCError
import TRPCError">TRPCError</data-lsp> } </span><span style="color: #D32F2F">from</span><span style="color: #24292EFF"> </span><span style="color: #22863A">'@trpc/server'</span><span style="color: #24292EFF">;</span></div><div class="line"><span style="color: #D32F2F">import</span><span style="color: #24292EFF"> { <data-lsp lsp="(alias) function experimental_nextAppDirCaller<TContext, TMeta>(config: Simplify<{
    pathExtractor?: (opts: {
        meta: TMeta;
    }) => string;
    normalizeFormData?: boolean;
    onError?: (opts: ErrorHandlerOptions<TContext>) => void;
} &amp; CreateContextCallback<TContext, () => MaybePromise<TContext>>>): CallerOverride<TContext>
import experimental_nextAppDirCaller">experimental_nextAppDirCaller</data-lsp> } </span><span style="color: #D32F2F">from</span><span style="color: #24292EFF"> </span><span style="color: #22863A">'@trpc/server/adapters/next-app-dir'</span><span style="color: #24292EFF">;</span></div><div class="line"><span style="color: #D32F2F">import</span><span style="color: #24292EFF"> { <data-lsp lsp="(alias) function currentUser(): Promise<User | null>
import currentUser">currentUser</data-lsp> } </span><span style="color: #D32F2F">from</span><span style="color: #24292EFF"> </span><span style="color: #22863A">'./auth'</span><span style="color: #24292EFF">;</span></div><div class="line">&nbsp;</div><div class="line"><span style="color: #D32F2F">interface</span><span style="color: #24292EFF"> </span><span style="color: #6F42C1"><data-lsp lsp="interface Meta">Meta</data-lsp></span><span style="color: #24292EFF"> {</span></div><div class="line"><span style="color: #24292EFF">  <data-lsp lsp="(property) Meta.span: string">span</data-lsp></span><span style="color: #D32F2F">:</span><span style="color: #24292EFF"> </span><span style="color: #1976D2">string</span><span style="color: #24292EFF">;</span></div><div class="line"><span style="color: #24292EFF">}</span></div><div class="line">&nbsp;</div><div class="line"><span style="color: #D32F2F">export</span><span style="color: #24292EFF"> </span><span style="color: #D32F2F">const</span><span style="color: #24292EFF"> </span><span style="color: #1976D2"><data-lsp lsp="const t: TRPCRootObject<object, Meta, RuntimeConfigOptions<object, Meta>, {
    ctx: object;
    meta: Meta;
    errorShape: DefaultErrorShape;
    transformer: false;
}>">t</data-lsp></span><span style="color: #24292EFF"> </span><span style="color: #D32F2F">=</span><span style="color: #24292EFF"> </span><span style="color: #1976D2"><data-lsp lsp="(alias) const initTRPC: TRPCBuilder<object, object>
import initTRPC">initTRPC</data-lsp></span><span style="color: #6F42C1">.<data-lsp lsp="(method) TRPCBuilder<object, object>.meta<Meta>(): TRPCBuilder<object, Meta>">meta</data-lsp></span><span style="color: #24292EFF">&lt;</span><span style="color: #6F42C1"><data-lsp lsp="interface Meta">Meta</data-lsp></span><span style="color: #24292EFF">&gt;()</span><span style="color: #6F42C1">.<data-lsp lsp="(method) TRPCBuilder<object, Meta>.create<RuntimeConfigOptions<object, Meta>>(opts?: RuntimeConfigOptions<object, Meta> | undefined): TRPCRootObject<object, Meta, RuntimeConfigOptions<object, Meta>, {
    ctx: object;
    meta: Meta;
    errorShape: DefaultErrorShape;
    transformer: false;
}>">create</data-lsp></span><span style="color: #24292EFF">();</span></div><div class="line">&nbsp;</div><div class="line"><span style="color: #D32F2F">export</span><span style="color: #24292EFF"> </span><span style="color: #D32F2F">const</span><span style="color: #24292EFF"> </span><span style="color: #1976D2"><data-lsp lsp="const serverActionProcedure: ProcedureBuilder<object, Meta, {
    user: User | null;
}, UnsetMarker, UnsetMarker, UnsetMarker, UnsetMarker, true>">serverActionProcedure</data-lsp></span><span style="color: #24292EFF"> </span><span style="color: #D32F2F">=</span><span style="color: #24292EFF"> </span><span style="color: #1976D2"><data-lsp lsp="const t: TRPCRootObject<object, Meta, RuntimeConfigOptions<object, Meta>, {
    ctx: object;
    meta: Meta;
    errorShape: DefaultErrorShape;
    transformer: false;
}>">t</data-lsp></span><span style="color: #24292EFF">.<data-lsp lsp="(property) TRPCRootObject<object, Meta, RuntimeConfigOptions<object, Meta>, { ctx: object; meta: Meta; errorShape: DefaultErrorShape; transformer: false; }>.procedure: ProcedureBuilder<object, Meta, object, UnsetMarker, UnsetMarker, UnsetMarker, UnsetMarker, false>">procedure</data-lsp></span></div><div class="line"><span style="color: #24292EFF">  </span><span style="color: #6F42C1">.<data-lsp lsp="(method) ProcedureBuilder<object, Meta, object, UnsetMarker, UnsetMarker, UnsetMarker, UnsetMarker, false>.experimental_caller(caller: CallerOverride<object>): ProcedureBuilder<object, Meta, object, UnsetMarker, UnsetMarker, UnsetMarker, UnsetMarker, true>">experimental_caller</data-lsp></span><span style="color: #24292EFF">(</span></div><div class="line"><span style="color: #24292EFF">    </span><span style="color: #6F42C1"><data-lsp lsp="(alias) experimental_nextAppDirCaller<object, unknown>(config: {
    pathExtractor?: ((opts: {
        meta: unknown;
    }) => string) | undefined;
    normalizeFormData?: boolean | undefined;
    onError?: ((opts: ErrorHandlerOptions<object>) => void) | undefined;
    createContext?: (() => MaybePromise<object>) | undefined;
}): CallerOverride<object>
import experimental_nextAppDirCaller">experimental_nextAppDirCaller</data-lsp></span><span style="color: #24292EFF">({</span></div><div class="line"><span style="color: #24292EFF">      </span><span style="color: #6F42C1"><data-lsp lsp="(property) pathExtractor?: ((opts: {
    meta: unknown;
}) => string) | undefined">pathExtractor</data-lsp></span><span style="color: #D32F2F">:</span><span style="color: #24292EFF"> ({ <data-lsp lsp="(parameter) meta: unknown">meta</data-lsp> }) </span><span style="color: #D32F2F">=&gt;</span><span style="color: #24292EFF"> (<data-lsp lsp="(parameter) meta: unknown">meta</data-lsp> </span><span style="color: #D32F2F">as</span><span style="color: #24292EFF"> </span><span style="color: #6F42C1"><data-lsp lsp="interface Meta">Meta</data-lsp></span><span style="color: #24292EFF">).<data-lsp lsp="(property) Meta.span: string">span</data-lsp></span><span style="color: #212121">,</span></div><div class="line"><span style="color: #24292EFF">    })</span><span style="color: #212121">,</span></div><div class="line"><span style="color: #24292EFF">  )</span></div><div class="line"><span style="color: #24292EFF">  </span><span style="color: #6F42C1">.<data-lsp lsp="(method) ProcedureBuilder<object, Meta, object, UnsetMarker, UnsetMarker, UnsetMarker, UnsetMarker, true>.use<{
    user: User | null;
}>(fn: MiddlewareBuilder<{}, Meta, {
    user: User | null;
}, UnsetMarker> | MiddlewareFunction<object, Meta, object, {
    user: User | null;
}, UnsetMarker>): ProcedureBuilder<object, Meta, {
    user: User | null;
}, UnsetMarker, UnsetMarker, UnsetMarker, UnsetMarker, true>">use</data-lsp></span><span style="color: #24292EFF">(</span><span style="color: #D32F2F">async</span><span style="color: #24292EFF"> (<data-lsp lsp="(parameter) opts: {
    ctx: {};
    type: ProcedureType;
    path: string;
    input: UnsetMarker;
    getRawInput: GetRawInputFn;
    meta: Meta | undefined;
    signal: AbortSignal | undefined;
    batchIndex: number;
    next: {
        (): Promise<MiddlewareResult<object>>;
        <$ContextOverride>(opts: {
            ctx?: $ContextOverride | undefined;
            input?: unknown;
        }): Promise<MiddlewareResult<$ContextOverride>>;
        (opts: {
            getRawInput: GetRawInputFn;
        }): Promise<...>;
    };
}">opts</data-lsp>) </span><span style="color: #D32F2F">=&gt;</span><span style="color: #24292EFF"> {</span></div><div class="line"><span style="color: #24292EFF">    </span><span style="color: #C2C3C5">// Inject user into context</span></div><div class="line"><span style="color: #24292EFF">    </span><span style="color: #D32F2F">const</span><span style="color: #24292EFF"> </span><span style="color: #1976D2"><data-lsp lsp="const user: User | null">user</data-lsp></span><span style="color: #24292EFF"> </span><span style="color: #D32F2F">=</span><span style="color: #24292EFF"> </span><span style="color: #D32F2F">await</span><span style="color: #24292EFF"> </span><span style="color: #6F42C1"><data-lsp lsp="(alias) currentUser(): Promise<User | null>
import currentUser">currentUser</data-lsp></span><span style="color: #24292EFF">();</span></div><div class="line"><span style="color: #24292EFF">    </span><span style="color: #D32F2F">return</span><span style="color: #24292EFF"> </span><span style="color: #1976D2"><data-lsp lsp="(parameter) opts: {
    ctx: {};
    type: ProcedureType;
    path: string;
    input: UnsetMarker;
    getRawInput: GetRawInputFn;
    meta: Meta | undefined;
    signal: AbortSignal | undefined;
    batchIndex: number;
    next: {
        (): Promise<MiddlewareResult<object>>;
        <$ContextOverride>(opts: {
            ctx?: $ContextOverride | undefined;
            input?: unknown;
        }): Promise<MiddlewareResult<$ContextOverride>>;
        (opts: {
            getRawInput: GetRawInputFn;
        }): Promise<...>;
    };
}">opts</data-lsp></span><span style="color: #6F42C1">.<data-lsp lsp="(property) next: <{
    user: User | null;
}>(opts: {
    ctx?: {
        user: User | null;
    } | undefined;
    input?: unknown;
}) => Promise<MiddlewareResult<{
    user: User | null;
}>> (+2 overloads)">next</data-lsp></span><span style="color: #24292EFF">({ <data-lsp lsp="(property) ctx?: {
    user: User | null;
} | undefined">ctx</data-lsp></span><span style="color: #D32F2F">:</span><span style="color: #24292EFF"> { <data-lsp lsp="(property) user: User | null">user</data-lsp> } });</span></div><div class="line"><span style="color: #24292EFF">  });</span></div></code></div><button type="button" aria-label="Copy code to clipboard" class="copy-button" onclick="navigator.clipboard.writeText(this.previousSibling.innerText),this.classList.add(&quot;copied&quot;),this.textContent=&quot;Copied&quot;,setTimeout(()=>{this.classList.remove(&quot;copied&quot;),this.textContent=&quot;Copy&quot;},2e3)">Copy</button></pre>
<pre class="shiki github-dark with-title twoslash lsp" style="background-color: #0d1117; color: #c9d1d9" title="server/trpc.ts"><div class="code-title">server/trpc.ts</div><div class="language-id">ts</div><div class="code-container"><code><div class="line"><span style="color: #FF7B72">import</span><span style="color: #C9D1D9"> { <data-lsp lsp="(alias) const initTRPC: TRPCBuilder<object, object>
import initTRPC">initTRPC</data-lsp>, <data-lsp lsp="(alias) class TRPCError
import TRPCError">TRPCError</data-lsp> } </span><span style="color: #FF7B72">from</span><span style="color: #C9D1D9"> </span><span style="color: #A5D6FF">'@trpc/server'</span><span style="color: #C9D1D9">;</span></div><div class="line"><span style="color: #FF7B72">import</span><span style="color: #C9D1D9"> { <data-lsp lsp="(alias) function experimental_nextAppDirCaller<TContext, TMeta>(config: Simplify<{
    pathExtractor?: (opts: {
        meta: TMeta;
    }) => string;
    normalizeFormData?: boolean;
    onError?: (opts: ErrorHandlerOptions<TContext>) => void;
} &amp; CreateContextCallback<TContext, () => MaybePromise<TContext>>>): CallerOverride<TContext>
import experimental_nextAppDirCaller">experimental_nextAppDirCaller</data-lsp> } </span><span style="color: #FF7B72">from</span><span style="color: #C9D1D9"> </span><span style="color: #A5D6FF">'@trpc/server/adapters/next-app-dir'</span><span style="color: #C9D1D9">;</span></div><div class="line"><span style="color: #FF7B72">import</span><span style="color: #C9D1D9"> { <data-lsp lsp="(alias) function currentUser(): Promise<User | null>
import currentUser">currentUser</data-lsp> } </span><span style="color: #FF7B72">from</span><span style="color: #C9D1D9"> </span><span style="color: #A5D6FF">'./auth'</span><span style="color: #C9D1D9">;</span></div><div class="line">&nbsp;</div><div class="line"><span style="color: #FF7B72">interface</span><span style="color: #C9D1D9"> </span><span style="color: #FFA657"><data-lsp lsp="interface Meta">Meta</data-lsp></span><span style="color: #C9D1D9"> {</span></div><div class="line"><span style="color: #C9D1D9">  </span><span style="color: #FFA657"><data-lsp lsp="(property) Meta.span: string">span</data-lsp></span><span style="color: #FF7B72">:</span><span style="color: #C9D1D9"> </span><span style="color: #79C0FF">string</span><span style="color: #C9D1D9">;</span></div><div class="line"><span style="color: #C9D1D9">}</span></div><div class="line">&nbsp;</div><div class="line"><span style="color: #FF7B72">export</span><span style="color: #C9D1D9"> </span><span style="color: #FF7B72">const</span><span style="color: #C9D1D9"> </span><span style="color: #79C0FF"><data-lsp lsp="const t: TRPCRootObject<object, Meta, RuntimeConfigOptions<object, Meta>, {
    ctx: object;
    meta: Meta;
    errorShape: DefaultErrorShape;
    transformer: false;
}>">t</data-lsp></span><span style="color: #C9D1D9"> </span><span style="color: #FF7B72">=</span><span style="color: #C9D1D9"> <data-lsp lsp="(alias) const initTRPC: TRPCBuilder<object, object>
import initTRPC">initTRPC</data-lsp>.</span><span style="color: #D2A8FF"><data-lsp lsp="(method) TRPCBuilder<object, object>.meta<Meta>(): TRPCBuilder<object, Meta>">meta</data-lsp></span><span style="color: #C9D1D9">&lt;</span><span style="color: #FFA657"><data-lsp lsp="interface Meta">Meta</data-lsp></span><span style="color: #C9D1D9">&gt;().</span><span style="color: #D2A8FF"><data-lsp lsp="(method) TRPCBuilder<object, Meta>.create<RuntimeConfigOptions<object, Meta>>(opts?: RuntimeConfigOptions<object, Meta> | undefined): TRPCRootObject<object, Meta, RuntimeConfigOptions<object, Meta>, {
    ctx: object;
    meta: Meta;
    errorShape: DefaultErrorShape;
    transformer: false;
}>">create</data-lsp></span><span style="color: #C9D1D9">();</span></div><div class="line">&nbsp;</div><div class="line"><span style="color: #FF7B72">export</span><span style="color: #C9D1D9"> </span><span style="color: #FF7B72">const</span><span style="color: #C9D1D9"> </span><span style="color: #79C0FF"><data-lsp lsp="const serverActionProcedure: ProcedureBuilder<object, Meta, {
    user: User | null;
}, UnsetMarker, UnsetMarker, UnsetMarker, UnsetMarker, true>">serverActionProcedure</data-lsp></span><span style="color: #C9D1D9"> </span><span style="color: #FF7B72">=</span><span style="color: #C9D1D9"> <data-lsp lsp="const t: TRPCRootObject<object, Meta, RuntimeConfigOptions<object, Meta>, {
    ctx: object;
    meta: Meta;
    errorShape: DefaultErrorShape;
    transformer: false;
}>">t</data-lsp>.<data-lsp lsp="(property) TRPCRootObject<object, Meta, RuntimeConfigOptions<object, Meta>, { ctx: object; meta: Meta; errorShape: DefaultErrorShape; transformer: false; }>.procedure: ProcedureBuilder<object, Meta, object, UnsetMarker, UnsetMarker, UnsetMarker, UnsetMarker, false>">procedure</data-lsp></span></div><div class="line"><span style="color: #C9D1D9">  .</span><span style="color: #D2A8FF"><data-lsp lsp="(method) ProcedureBuilder<object, Meta, object, UnsetMarker, UnsetMarker, UnsetMarker, UnsetMarker, false>.experimental_caller(caller: CallerOverride<object>): ProcedureBuilder<object, Meta, object, UnsetMarker, UnsetMarker, UnsetMarker, UnsetMarker, true>">experimental_caller</data-lsp></span><span style="color: #C9D1D9">(</span></div><div class="line"><span style="color: #C9D1D9">    </span><span style="color: #D2A8FF"><data-lsp lsp="(alias) experimental_nextAppDirCaller<object, unknown>(config: {
    pathExtractor?: ((opts: {
        meta: unknown;
    }) => string) | undefined;
    normalizeFormData?: boolean | undefined;
    onError?: ((opts: ErrorHandlerOptions<object>) => void) | undefined;
    createContext?: (() => MaybePromise<object>) | undefined;
}): CallerOverride<object>
import experimental_nextAppDirCaller">experimental_nextAppDirCaller</data-lsp></span><span style="color: #C9D1D9">({</span></div><div class="line"><span style="color: #C9D1D9">      </span><span style="color: #D2A8FF"><data-lsp lsp="(property) pathExtractor?: ((opts: {
    meta: unknown;
}) => string) | undefined">pathExtractor</data-lsp></span><span style="color: #C9D1D9">: ({ </span><span style="color: #FFA657"><data-lsp lsp="(parameter) meta: unknown">meta</data-lsp></span><span style="color: #C9D1D9"> }) </span><span style="color: #FF7B72">=&gt;</span><span style="color: #C9D1D9"> (<data-lsp lsp="(parameter) meta: unknown">meta</data-lsp> </span><span style="color: #FF7B72">as</span><span style="color: #C9D1D9"> </span><span style="color: #FFA657"><data-lsp lsp="interface Meta">Meta</data-lsp></span><span style="color: #C9D1D9">).<data-lsp lsp="(property) Meta.span: string">span</data-lsp>,</span></div><div class="line"><span style="color: #C9D1D9">    }),</span></div><div class="line"><span style="color: #C9D1D9">  )</span></div><div class="line"><span style="color: #C9D1D9">  .</span><span style="color: #D2A8FF"><data-lsp lsp="(method) ProcedureBuilder<object, Meta, object, UnsetMarker, UnsetMarker, UnsetMarker, UnsetMarker, true>.use<{
    user: User | null;
}>(fn: MiddlewareBuilder<{}, Meta, {
    user: User | null;
}, UnsetMarker> | MiddlewareFunction<object, Meta, object, {
    user: User | null;
}, UnsetMarker>): ProcedureBuilder<object, Meta, {
    user: User | null;
}, UnsetMarker, UnsetMarker, UnsetMarker, UnsetMarker, true>">use</data-lsp></span><span style="color: #C9D1D9">(</span><span style="color: #FF7B72">async</span><span style="color: #C9D1D9"> (</span><span style="color: #FFA657"><data-lsp lsp="(parameter) opts: {
    ctx: {};
    type: ProcedureType;
    path: string;
    input: UnsetMarker;
    getRawInput: GetRawInputFn;
    meta: Meta | undefined;
    signal: AbortSignal | undefined;
    batchIndex: number;
    next: {
        (): Promise<MiddlewareResult<object>>;
        <$ContextOverride>(opts: {
            ctx?: $ContextOverride | undefined;
            input?: unknown;
        }): Promise<MiddlewareResult<$ContextOverride>>;
        (opts: {
            getRawInput: GetRawInputFn;
        }): Promise<...>;
    };
}">opts</data-lsp></span><span style="color: #C9D1D9">) </span><span style="color: #FF7B72">=&gt;</span><span style="color: #C9D1D9"> {</span></div><div class="line"><span style="color: #C9D1D9">    </span><span style="color: #8B949E">// Inject user into context</span></div><div class="line"><span style="color: #C9D1D9">    </span><span style="color: #FF7B72">const</span><span style="color: #C9D1D9"> </span><span style="color: #79C0FF"><data-lsp lsp="const user: User | null">user</data-lsp></span><span style="color: #C9D1D9"> </span><span style="color: #FF7B72">=</span><span style="color: #C9D1D9"> </span><span style="color: #FF7B72">await</span><span style="color: #C9D1D9"> </span><span style="color: #D2A8FF"><data-lsp lsp="(alias) currentUser(): Promise<User | null>
import currentUser">currentUser</data-lsp></span><span style="color: #C9D1D9">();</span></div><div class="line"><span style="color: #C9D1D9">    </span><span style="color: #FF7B72">return</span><span style="color: #C9D1D9"> <data-lsp lsp="(parameter) opts: {
    ctx: {};
    type: ProcedureType;
    path: string;
    input: UnsetMarker;
    getRawInput: GetRawInputFn;
    meta: Meta | undefined;
    signal: AbortSignal | undefined;
    batchIndex: number;
    next: {
        (): Promise<MiddlewareResult<object>>;
        <$ContextOverride>(opts: {
            ctx?: $ContextOverride | undefined;
            input?: unknown;
        }): Promise<MiddlewareResult<$ContextOverride>>;
        (opts: {
            getRawInput: GetRawInputFn;
        }): Promise<...>;
    };
}">opts</data-lsp>.</span><span style="color: #D2A8FF"><data-lsp lsp="(property) next: <{
    user: User | null;
}>(opts: {
    ctx?: {
        user: User | null;
    } | undefined;
    input?: unknown;
}) => Promise<MiddlewareResult<{
    user: User | null;
}>> (+2 overloads)">next</data-lsp></span><span style="color: #C9D1D9">({ <data-lsp lsp="(property) ctx?: {
    user: User | null;
} | undefined">ctx</data-lsp>: { <data-lsp lsp="(property) user: User | null">user</data-lsp> } });</span></div><div class="line"><span style="color: #C9D1D9">  });</span></div></code></div><button type="button" aria-label="Copy code to clipboard" class="copy-button" onclick="navigator.clipboard.writeText(this.previousSibling.innerText),this.classList.add(&quot;copied&quot;),this.textContent=&quot;Copied&quot;,setTimeout(()=>{this.classList.remove(&quot;copied&quot;),this.textContent=&quot;Copy&quot;},2e3)">Copy</button></pre></div>
<p>Slutligen skapar vi en <code>protectedAction</code>-procedur som skyddar alla åtgärder från oautentiserade användare. Om du redan har en middleware som gör detta kan du använda den, men jag definierar en inline i det här exemplet.</p>
<div></div>
<div><pre class="shiki min-light with-title twoslash lsp" style="background-color: #ffffff; color: #24292eff" title="server/trpc.ts"><div class="code-title">server/trpc.ts</div><div class="language-id">ts</div><div class="code-container"><code><div class="line"><span style="color: #D32F2F">import</span><span style="color: #24292EFF"> { <data-lsp lsp="(alias) const initTRPC: TRPCBuilder<object, object>
import initTRPC">initTRPC</data-lsp></span><span style="color: #212121">,</span><span style="color: #24292EFF"> <data-lsp lsp="(alias) class TRPCError
import TRPCError">TRPCError</data-lsp> } </span><span style="color: #D32F2F">from</span><span style="color: #24292EFF"> </span><span style="color: #22863A">'@trpc/server'</span><span style="color: #24292EFF">;</span></div><div class="line"><span style="color: #D32F2F">import</span><span style="color: #24292EFF"> { <data-lsp lsp="(alias) function experimental_nextAppDirCaller<TContext, TMeta>(config: Simplify<{
    pathExtractor?: (opts: {
        meta: TMeta;
    }) => string;
    normalizeFormData?: boolean;
    onError?: (opts: ErrorHandlerOptions<TContext>) => void;
} &amp; CreateContextCallback<TContext, () => MaybePromise<TContext>>>): CallerOverride<TContext>
import experimental_nextAppDirCaller">experimental_nextAppDirCaller</data-lsp> } </span><span style="color: #D32F2F">from</span><span style="color: #24292EFF"> </span><span style="color: #22863A">'@trpc/server/adapters/next-app-dir'</span><span style="color: #24292EFF">;</span></div><div class="line"><span style="color: #D32F2F">import</span><span style="color: #24292EFF"> { <data-lsp lsp="(alias) function currentUser(): Promise<User | null>
import currentUser">currentUser</data-lsp> } </span><span style="color: #D32F2F">from</span><span style="color: #24292EFF"> </span><span style="color: #22863A">'./auth'</span><span style="color: #24292EFF">;</span></div><div class="line">&nbsp;</div><div class="line"><span style="color: #D32F2F">interface</span><span style="color: #24292EFF"> </span><span style="color: #6F42C1"><data-lsp lsp="interface Meta">Meta</data-lsp></span><span style="color: #24292EFF"> {</span></div><div class="line"><span style="color: #24292EFF">  <data-lsp lsp="(property) Meta.span: string">span</data-lsp></span><span style="color: #D32F2F">:</span><span style="color: #24292EFF"> </span><span style="color: #1976D2">string</span><span style="color: #24292EFF">;</span></div><div class="line"><span style="color: #24292EFF">}</span></div><div class="line">&nbsp;</div><div class="line"><span style="color: #D32F2F">export</span><span style="color: #24292EFF"> </span><span style="color: #D32F2F">const</span><span style="color: #24292EFF"> </span><span style="color: #1976D2"><data-lsp lsp="const t: TRPCRootObject<object, Meta, RuntimeConfigOptions<object, Meta>, {
    ctx: object;
    meta: Meta;
    errorShape: DefaultErrorShape;
    transformer: false;
}>">t</data-lsp></span><span style="color: #24292EFF"> </span><span style="color: #D32F2F">=</span><span style="color: #24292EFF"> </span><span style="color: #1976D2"><data-lsp lsp="(alias) const initTRPC: TRPCBuilder<object, object>
import initTRPC">initTRPC</data-lsp></span><span style="color: #6F42C1">.<data-lsp lsp="(method) TRPCBuilder<object, object>.meta<Meta>(): TRPCBuilder<object, Meta>">meta</data-lsp></span><span style="color: #24292EFF">&lt;</span><span style="color: #6F42C1"><data-lsp lsp="interface Meta">Meta</data-lsp></span><span style="color: #24292EFF">&gt;()</span><span style="color: #6F42C1">.<data-lsp lsp="(method) TRPCBuilder<object, Meta>.create<RuntimeConfigOptions<object, Meta>>(opts?: RuntimeConfigOptions<object, Meta> | undefined): TRPCRootObject<object, Meta, RuntimeConfigOptions<object, Meta>, {
    ctx: object;
    meta: Meta;
    errorShape: DefaultErrorShape;
    transformer: false;
}>">create</data-lsp></span><span style="color: #24292EFF">();</span></div><div class="line">&nbsp;</div><div class="line"><span style="color: #D32F2F">export</span><span style="color: #24292EFF"> </span><span style="color: #D32F2F">const</span><span style="color: #24292EFF"> </span><span style="color: #1976D2"><data-lsp lsp="const serverActionProcedure: ProcedureBuilder<object, Meta, {
    user: User | null;
}, UnsetMarker, UnsetMarker, UnsetMarker, UnsetMarker, true>">serverActionProcedure</data-lsp></span><span style="color: #24292EFF"> </span><span style="color: #D32F2F">=</span><span style="color: #24292EFF"> </span><span style="color: #1976D2"><data-lsp lsp="const t: TRPCRootObject<object, Meta, RuntimeConfigOptions<object, Meta>, {
    ctx: object;
    meta: Meta;
    errorShape: DefaultErrorShape;
    transformer: false;
}>">t</data-lsp></span><span style="color: #24292EFF">.<data-lsp lsp="(property) TRPCRootObject<object, Meta, RuntimeConfigOptions<object, Meta>, { ctx: object; meta: Meta; errorShape: DefaultErrorShape; transformer: false; }>.procedure: ProcedureBuilder<object, Meta, object, UnsetMarker, UnsetMarker, UnsetMarker, UnsetMarker, false>">procedure</data-lsp></span></div><div class="line"><span style="color: #24292EFF">  </span><span style="color: #6F42C1">.<data-lsp lsp="(method) ProcedureBuilder<object, Meta, object, UnsetMarker, UnsetMarker, UnsetMarker, UnsetMarker, false>.experimental_caller(caller: CallerOverride<object>): ProcedureBuilder<object, Meta, object, UnsetMarker, UnsetMarker, UnsetMarker, UnsetMarker, true>">experimental_caller</data-lsp></span><span style="color: #24292EFF">(</span></div><div class="line"><span style="color: #24292EFF">    </span><span style="color: #6F42C1"><data-lsp lsp="(alias) experimental_nextAppDirCaller<object, unknown>(config: {
    pathExtractor?: ((opts: {
        meta: unknown;
    }) => string) | undefined;
    normalizeFormData?: boolean | undefined;
    onError?: ((opts: ErrorHandlerOptions<object>) => void) | undefined;
    createContext?: (() => MaybePromise<object>) | undefined;
}): CallerOverride<object>
import experimental_nextAppDirCaller">experimental_nextAppDirCaller</data-lsp></span><span style="color: #24292EFF">({</span></div><div class="line"><span style="color: #24292EFF">      </span><span style="color: #6F42C1"><data-lsp lsp="(property) pathExtractor?: ((opts: {
    meta: unknown;
}) => string) | undefined">pathExtractor</data-lsp></span><span style="color: #D32F2F">:</span><span style="color: #24292EFF"> ({ <data-lsp lsp="(parameter) meta: unknown">meta</data-lsp> }) </span><span style="color: #D32F2F">=&gt;</span><span style="color: #24292EFF"> (<data-lsp lsp="(parameter) meta: unknown">meta</data-lsp> </span><span style="color: #D32F2F">as</span><span style="color: #24292EFF"> </span><span style="color: #6F42C1"><data-lsp lsp="interface Meta">Meta</data-lsp></span><span style="color: #24292EFF">).<data-lsp lsp="(property) Meta.span: string">span</data-lsp></span><span style="color: #212121">,</span></div><div class="line"><span style="color: #24292EFF">    })</span><span style="color: #212121">,</span></div><div class="line"><span style="color: #24292EFF">  )</span></div><div class="line"><span style="color: #24292EFF">  </span><span style="color: #6F42C1">.<data-lsp lsp="(method) ProcedureBuilder<object, Meta, object, UnsetMarker, UnsetMarker, UnsetMarker, UnsetMarker, true>.use<{
    user: User | null;
}>(fn: MiddlewareBuilder<{}, Meta, {
    user: User | null;
}, UnsetMarker> | MiddlewareFunction<object, Meta, object, {
    user: User | null;
}, UnsetMarker>): ProcedureBuilder<object, Meta, {
    user: User | null;
}, UnsetMarker, UnsetMarker, UnsetMarker, UnsetMarker, true>">use</data-lsp></span><span style="color: #24292EFF">(</span><span style="color: #D32F2F">async</span><span style="color: #24292EFF"> (<data-lsp lsp="(parameter) opts: {
    ctx: {};
    type: ProcedureType;
    path: string;
    input: UnsetMarker;
    getRawInput: GetRawInputFn;
    meta: Meta | undefined;
    signal: AbortSignal | undefined;
    batchIndex: number;
    next: {
        (): Promise<MiddlewareResult<object>>;
        <$ContextOverride>(opts: {
            ctx?: $ContextOverride | undefined;
            input?: unknown;
        }): Promise<MiddlewareResult<$ContextOverride>>;
        (opts: {
            getRawInput: GetRawInputFn;
        }): Promise<...>;
    };
}">opts</data-lsp>) </span><span style="color: #D32F2F">=&gt;</span><span style="color: #24292EFF"> {</span></div><div class="line"><span style="color: #24292EFF">    </span><span style="color: #C2C3C5">// Inject user into context</span></div><div class="line"><span style="color: #24292EFF">    </span><span style="color: #D32F2F">const</span><span style="color: #24292EFF"> </span><span style="color: #1976D2"><data-lsp lsp="const user: User | null" style="border-bottom: solid 2px lightgrey;">user</data-lsp></span><span style="color: #24292EFF"> </span><span style="color: #D32F2F">=</span><span style="color: #24292EFF"> </span><span style="color: #D32F2F">await</span><span style="color: #24292EFF"> </span><span style="color: #6F42C1"><data-lsp lsp="(alias) currentUser(): Promise<User | null>
import currentUser">currentUser</data-lsp></span><span style="color: #24292EFF">();</span></div><div class="meta-line"><span class="popover-prefix">           </span><span class="popover"><div class="arrow"></div>const user: User | null</span></div><div class="line"><span style="color: #24292EFF">    </span><span style="color: #D32F2F">return</span><span style="color: #24292EFF"> </span><span style="color: #1976D2"><data-lsp lsp="(parameter) opts: {
    ctx: {};
    type: ProcedureType;
    path: string;
    input: UnsetMarker;
    getRawInput: GetRawInputFn;
    meta: Meta | undefined;
    signal: AbortSignal | undefined;
    batchIndex: number;
    next: {
        (): Promise<MiddlewareResult<object>>;
        <$ContextOverride>(opts: {
            ctx?: $ContextOverride | undefined;
            input?: unknown;
        }): Promise<MiddlewareResult<$ContextOverride>>;
        (opts: {
            getRawInput: GetRawInputFn;
        }): Promise<...>;
    };
}">opts</data-lsp></span><span style="color: #6F42C1">.<data-lsp lsp="(property) next: <{
    user: User | null;
}>(opts: {
    ctx?: {
        user: User | null;
    } | undefined;
    input?: unknown;
}) => Promise<MiddlewareResult<{
    user: User | null;
}>> (+2 overloads)">next</data-lsp></span><span style="color: #24292EFF">({ <data-lsp lsp="(property) ctx?: {
    user: User | null;
} | undefined">ctx</data-lsp></span><span style="color: #D32F2F">:</span><span style="color: #24292EFF"> { <data-lsp lsp="(property) user: User | null">user</data-lsp> } });</span></div><div class="line"><span style="color: #24292EFF">  });</span></div><div class="line">&nbsp;</div><div class="line"><span style="color: #D32F2F">export</span><span style="color: #24292EFF"> </span><span style="color: #D32F2F">const</span><span style="color: #24292EFF"> </span><span style="color: #1976D2"><data-lsp lsp="const protectedAction: ProcedureBuilder<object, Meta, {
    user: User;
}, UnsetMarker, UnsetMarker, UnsetMarker, UnsetMarker, true>">protectedAction</data-lsp></span><span style="color: #24292EFF"> </span><span style="color: #D32F2F">=</span><span style="color: #24292EFF"> </span><span style="color: #1976D2"><data-lsp lsp="const serverActionProcedure: ProcedureBuilder<object, Meta, {
    user: User | null;
}, UnsetMarker, UnsetMarker, UnsetMarker, UnsetMarker, true>">serverActionProcedure</data-lsp></span><span style="color: #6F42C1">.<data-lsp lsp="(method) ProcedureBuilder<object, Meta, { user: User | null; }, UnsetMarker, UnsetMarker, UnsetMarker, UnsetMarker, true>.use<{
    user: User;
}>(fn: MiddlewareBuilder<{
    user: User | null;
}, Meta, {
    user: User;
}, UnsetMarker> | MiddlewareFunction<object, Meta, {
    user: User | null;
}, {
    user: User;
}, UnsetMarker>): ProcedureBuilder<object, Meta, {
    user: User;
}, UnsetMarker, UnsetMarker, UnsetMarker, UnsetMarker, true>">use</data-lsp></span><span style="color: #24292EFF">((<data-lsp lsp="(parameter) opts: {
    ctx: {
        user: User | null;
    };
    type: ProcedureType;
    path: string;
    input: UnsetMarker;
    getRawInput: GetRawInputFn;
    meta: Meta | undefined;
    signal: AbortSignal | undefined;
    batchIndex: number;
    next: {
        (): Promise<MiddlewareResult<{
            user: User | null;
        }>>;
        <$ContextOverride>(opts: {
            ctx?: $ContextOverride | undefined;
            input?: unknown;
        }): Promise<MiddlewareResult<$ContextOverride>>;
        (opts: {
            getRawInput: GetRawInputFn;
        }): Promise<...>;
    };
}">opts</data-lsp>) </span><span style="color: #D32F2F">=&gt;</span><span style="color: #24292EFF"> {</span></div><div class="line"><span style="color: #24292EFF">  </span><span style="color: #D32F2F">if</span><span style="color: #24292EFF"> (</span><span style="color: #D32F2F">!</span><span style="color: #1976D2"><data-lsp lsp="(parameter) opts: {
    ctx: {
        user: User | null;
    };
    type: ProcedureType;
    path: string;
    input: UnsetMarker;
    getRawInput: GetRawInputFn;
    meta: Meta | undefined;
    signal: AbortSignal | undefined;
    batchIndex: number;
    next: {
        (): Promise<MiddlewareResult<{
            user: User | null;
        }>>;
        <$ContextOverride>(opts: {
            ctx?: $ContextOverride | undefined;
            input?: unknown;
        }): Promise<MiddlewareResult<$ContextOverride>>;
        (opts: {
            getRawInput: GetRawInputFn;
        }): Promise<...>;
    };
}">opts</data-lsp></span><span style="color: #24292EFF">.</span><span style="color: #1976D2"><data-lsp lsp="(property) ctx: {
    user: User | null;
}">ctx</data-lsp></span><span style="color: #24292EFF">.<data-lsp lsp="(property) user: User | null">user</data-lsp>) {</span></div><div class="line"><span style="color: #24292EFF">    </span><span style="color: #D32F2F">throw</span><span style="color: #24292EFF"> </span><span style="color: #D32F2F">new</span><span style="color: #24292EFF"> </span><span style="color: #6F42C1"><data-lsp lsp="(alias) new TRPCError(opts: {
    message?: string;
    code: TRPC_ERROR_CODE_KEY;
    cause?: unknown;
}): TRPCError
import TRPCError">TRPCError</data-lsp></span><span style="color: #24292EFF">({</span></div><div class="line"><span style="color: #24292EFF">      <data-lsp lsp="(property) code: &quot;UNAUTHORIZED&quot; | &quot;PARSE_ERROR&quot; | &quot;BAD_REQUEST&quot; | &quot;INTERNAL_SERVER_ERROR&quot; | &quot;NOT_IMPLEMENTED&quot; | &quot;BAD_GATEWAY&quot; | &quot;SERVICE_UNAVAILABLE&quot; | &quot;GATEWAY_TIMEOUT&quot; | &quot;PAYMENT_REQUIRED&quot; | &quot;FORBIDDEN&quot; | &quot;NOT_FOUND&quot; | &quot;METHOD_NOT_SUPPORTED&quot; | &quot;TIMEOUT&quot; | &quot;CONFLICT&quot; | &quot;PRECONDITION_FAILED&quot; | &quot;PAYLOAD_TOO_LARGE&quot; | &quot;UNSUPPORTED_MEDIA_TYPE&quot; | &quot;UNPROCESSABLE_CONTENT&quot; | &quot;PRECONDITION_REQUIRED&quot; | &quot;TOO_MANY_REQUESTS&quot; | &quot;CLIENT_CLOSED_REQUEST&quot;">code</data-lsp></span><span style="color: #D32F2F">:</span><span style="color: #24292EFF"> </span><span style="color: #22863A">'UNAUTHORIZED'</span><span style="color: #212121">,</span></div><div class="line"><span style="color: #24292EFF">    });</span></div><div class="line"><span style="color: #24292EFF">  }</span></div><div class="line">&nbsp;</div><div class="line"><span style="color: #24292EFF">  </span><span style="color: #D32F2F">return</span><span style="color: #24292EFF"> </span><span style="color: #1976D2"><data-lsp lsp="(parameter) opts: {
    ctx: {
        user: User | null;
    };
    type: ProcedureType;
    path: string;
    input: UnsetMarker;
    getRawInput: GetRawInputFn;
    meta: Meta | undefined;
    signal: AbortSignal | undefined;
    batchIndex: number;
    next: {
        (): Promise<MiddlewareResult<{
            user: User | null;
        }>>;
        <$ContextOverride>(opts: {
            ctx?: $ContextOverride | undefined;
            input?: unknown;
        }): Promise<MiddlewareResult<$ContextOverride>>;
        (opts: {
            getRawInput: GetRawInputFn;
        }): Promise<...>;
    };
}">opts</data-lsp></span><span style="color: #6F42C1">.<data-lsp lsp="(property) next: <{
    user: User;
}>(opts: {
    ctx?: {
        user: User;
    } | undefined;
    input?: unknown;
}) => Promise<MiddlewareResult<{
    user: User;
}>> (+2 overloads)">next</data-lsp></span><span style="color: #24292EFF">({</span></div><div class="line"><span style="color: #24292EFF">    <data-lsp lsp="(property) ctx?: {
    user: User;
} | undefined">ctx</data-lsp></span><span style="color: #D32F2F">:</span><span style="color: #24292EFF"> {</span></div><div class="line"><span style="color: #24292EFF">      </span><span style="color: #D32F2F">...</span><span style="color: #1976D2"><data-lsp lsp="(parameter) opts: {
    ctx: {
        user: User | null;
    };
    type: ProcedureType;
    path: string;
    input: UnsetMarker;
    getRawInput: GetRawInputFn;
    meta: Meta | undefined;
    signal: AbortSignal | undefined;
    batchIndex: number;
    next: {
        (): Promise<MiddlewareResult<{
            user: User | null;
        }>>;
        <$ContextOverride>(opts: {
            ctx?: $ContextOverride | undefined;
            input?: unknown;
        }): Promise<MiddlewareResult<$ContextOverride>>;
        (opts: {
            getRawInput: GetRawInputFn;
        }): Promise<...>;
    };
}">opts</data-lsp></span><span style="color: #24292EFF">.<data-lsp lsp="(property) ctx: {
    user: User | null;
}">ctx</data-lsp></span><span style="color: #212121">,</span></div><div class="line"><span style="color: #24292EFF">      <data-lsp lsp="(property) user: User" style="border-bottom: solid 2px lightgrey;">user</data-lsp></span><span style="color: #D32F2F">:</span><span style="color: #24292EFF"> </span><span style="color: #1976D2"><data-lsp lsp="(parameter) opts: {
    ctx: {
        user: User | null;
    };
    type: ProcedureType;
    path: string;
    input: UnsetMarker;
    getRawInput: GetRawInputFn;
    meta: Meta | undefined;
    signal: AbortSignal | undefined;
    batchIndex: number;
    next: {
        (): Promise<MiddlewareResult<{
            user: User | null;
        }>>;
        <$ContextOverride>(opts: {
            ctx?: $ContextOverride | undefined;
            input?: unknown;
        }): Promise<MiddlewareResult<$ContextOverride>>;
        (opts: {
            getRawInput: GetRawInputFn;
        }): Promise<...>;
    };
}">opts</data-lsp></span><span style="color: #24292EFF">.</span><span style="color: #1976D2"><data-lsp lsp="(property) ctx: {
    user: User | null;
}">ctx</data-lsp></span><span style="color: #24292EFF">.<data-lsp lsp="(property) user: User" style="border-bottom: solid 2px lightgrey;">user</data-lsp></span><span style="color: #212121">,</span><span style="color: #24292EFF"> </span><span style="color: #C2C3C5">// &lt;-- ensures type is non-nullable</span></div><div class="meta-line"><span class="popover-prefix">       </span><span class="popover"><div class="arrow"></div>(property) user: User</span></div><div class="line"><span style="color: #24292EFF">    }</span><span style="color: #212121">,</span></div><div class="line"><span style="color: #24292EFF">  });</span></div><div class="line"><span style="color: #24292EFF">});</span></div></code></div><button type="button" aria-label="Copy code to clipboard" class="copy-button" onclick="navigator.clipboard.writeText(this.previousSibling.innerText),this.classList.add(&quot;copied&quot;),this.textContent=&quot;Copied&quot;,setTimeout(()=>{this.classList.remove(&quot;copied&quot;),this.textContent=&quot;Copy&quot;},2e3)">Copy</button></pre>
<pre class="shiki github-dark with-title twoslash lsp" style="background-color: #0d1117; color: #c9d1d9" title="server/trpc.ts"><div class="code-title">server/trpc.ts</div><div class="language-id">ts</div><div class="code-container"><code><div class="line"><span style="color: #FF7B72">import</span><span style="color: #C9D1D9"> { <data-lsp lsp="(alias) const initTRPC: TRPCBuilder<object, object>
import initTRPC">initTRPC</data-lsp>, <data-lsp lsp="(alias) class TRPCError
import TRPCError">TRPCError</data-lsp> } </span><span style="color: #FF7B72">from</span><span style="color: #C9D1D9"> </span><span style="color: #A5D6FF">'@trpc/server'</span><span style="color: #C9D1D9">;</span></div><div class="line"><span style="color: #FF7B72">import</span><span style="color: #C9D1D9"> { <data-lsp lsp="(alias) function experimental_nextAppDirCaller<TContext, TMeta>(config: Simplify<{
    pathExtractor?: (opts: {
        meta: TMeta;
    }) => string;
    normalizeFormData?: boolean;
    onError?: (opts: ErrorHandlerOptions<TContext>) => void;
} &amp; CreateContextCallback<TContext, () => MaybePromise<TContext>>>): CallerOverride<TContext>
import experimental_nextAppDirCaller">experimental_nextAppDirCaller</data-lsp> } </span><span style="color: #FF7B72">from</span><span style="color: #C9D1D9"> </span><span style="color: #A5D6FF">'@trpc/server/adapters/next-app-dir'</span><span style="color: #C9D1D9">;</span></div><div class="line"><span style="color: #FF7B72">import</span><span style="color: #C9D1D9"> { <data-lsp lsp="(alias) function currentUser(): Promise<User | null>
import currentUser">currentUser</data-lsp> } </span><span style="color: #FF7B72">from</span><span style="color: #C9D1D9"> </span><span style="color: #A5D6FF">'./auth'</span><span style="color: #C9D1D9">;</span></div><div class="line">&nbsp;</div><div class="line"><span style="color: #FF7B72">interface</span><span style="color: #C9D1D9"> </span><span style="color: #FFA657"><data-lsp lsp="interface Meta">Meta</data-lsp></span><span style="color: #C9D1D9"> {</span></div><div class="line"><span style="color: #C9D1D9">  </span><span style="color: #FFA657"><data-lsp lsp="(property) Meta.span: string">span</data-lsp></span><span style="color: #FF7B72">:</span><span style="color: #C9D1D9"> </span><span style="color: #79C0FF">string</span><span style="color: #C9D1D9">;</span></div><div class="line"><span style="color: #C9D1D9">}</span></div><div class="line">&nbsp;</div><div class="line"><span style="color: #FF7B72">export</span><span style="color: #C9D1D9"> </span><span style="color: #FF7B72">const</span><span style="color: #C9D1D9"> </span><span style="color: #79C0FF"><data-lsp lsp="const t: TRPCRootObject<object, Meta, RuntimeConfigOptions<object, Meta>, {
    ctx: object;
    meta: Meta;
    errorShape: DefaultErrorShape;
    transformer: false;
}>">t</data-lsp></span><span style="color: #C9D1D9"> </span><span style="color: #FF7B72">=</span><span style="color: #C9D1D9"> <data-lsp lsp="(alias) const initTRPC: TRPCBuilder<object, object>
import initTRPC">initTRPC</data-lsp>.</span><span style="color: #D2A8FF"><data-lsp lsp="(method) TRPCBuilder<object, object>.meta<Meta>(): TRPCBuilder<object, Meta>">meta</data-lsp></span><span style="color: #C9D1D9">&lt;</span><span style="color: #FFA657"><data-lsp lsp="interface Meta">Meta</data-lsp></span><span style="color: #C9D1D9">&gt;().</span><span style="color: #D2A8FF"><data-lsp lsp="(method) TRPCBuilder<object, Meta>.create<RuntimeConfigOptions<object, Meta>>(opts?: RuntimeConfigOptions<object, Meta> | undefined): TRPCRootObject<object, Meta, RuntimeConfigOptions<object, Meta>, {
    ctx: object;
    meta: Meta;
    errorShape: DefaultErrorShape;
    transformer: false;
}>">create</data-lsp></span><span style="color: #C9D1D9">();</span></div><div class="line">&nbsp;</div><div class="line"><span style="color: #FF7B72">export</span><span style="color: #C9D1D9"> </span><span style="color: #FF7B72">const</span><span style="color: #C9D1D9"> </span><span style="color: #79C0FF"><data-lsp lsp="const serverActionProcedure: ProcedureBuilder<object, Meta, {
    user: User | null;
}, UnsetMarker, UnsetMarker, UnsetMarker, UnsetMarker, true>">serverActionProcedure</data-lsp></span><span style="color: #C9D1D9"> </span><span style="color: #FF7B72">=</span><span style="color: #C9D1D9"> <data-lsp lsp="const t: TRPCRootObject<object, Meta, RuntimeConfigOptions<object, Meta>, {
    ctx: object;
    meta: Meta;
    errorShape: DefaultErrorShape;
    transformer: false;
}>">t</data-lsp>.<data-lsp lsp="(property) TRPCRootObject<object, Meta, RuntimeConfigOptions<object, Meta>, { ctx: object; meta: Meta; errorShape: DefaultErrorShape; transformer: false; }>.procedure: ProcedureBuilder<object, Meta, object, UnsetMarker, UnsetMarker, UnsetMarker, UnsetMarker, false>">procedure</data-lsp></span></div><div class="line"><span style="color: #C9D1D9">  .</span><span style="color: #D2A8FF"><data-lsp lsp="(method) ProcedureBuilder<object, Meta, object, UnsetMarker, UnsetMarker, UnsetMarker, UnsetMarker, false>.experimental_caller(caller: CallerOverride<object>): ProcedureBuilder<object, Meta, object, UnsetMarker, UnsetMarker, UnsetMarker, UnsetMarker, true>">experimental_caller</data-lsp></span><span style="color: #C9D1D9">(</span></div><div class="line"><span style="color: #C9D1D9">    </span><span style="color: #D2A8FF"><data-lsp lsp="(alias) experimental_nextAppDirCaller<object, unknown>(config: {
    pathExtractor?: ((opts: {
        meta: unknown;
    }) => string) | undefined;
    normalizeFormData?: boolean | undefined;
    onError?: ((opts: ErrorHandlerOptions<object>) => void) | undefined;
    createContext?: (() => MaybePromise<object>) | undefined;
}): CallerOverride<object>
import experimental_nextAppDirCaller">experimental_nextAppDirCaller</data-lsp></span><span style="color: #C9D1D9">({</span></div><div class="line"><span style="color: #C9D1D9">      </span><span style="color: #D2A8FF"><data-lsp lsp="(property) pathExtractor?: ((opts: {
    meta: unknown;
}) => string) | undefined">pathExtractor</data-lsp></span><span style="color: #C9D1D9">: ({ </span><span style="color: #FFA657"><data-lsp lsp="(parameter) meta: unknown">meta</data-lsp></span><span style="color: #C9D1D9"> }) </span><span style="color: #FF7B72">=&gt;</span><span style="color: #C9D1D9"> (<data-lsp lsp="(parameter) meta: unknown">meta</data-lsp> </span><span style="color: #FF7B72">as</span><span style="color: #C9D1D9"> </span><span style="color: #FFA657"><data-lsp lsp="interface Meta">Meta</data-lsp></span><span style="color: #C9D1D9">).<data-lsp lsp="(property) Meta.span: string">span</data-lsp>,</span></div><div class="line"><span style="color: #C9D1D9">    }),</span></div><div class="line"><span style="color: #C9D1D9">  )</span></div><div class="line"><span style="color: #C9D1D9">  .</span><span style="color: #D2A8FF"><data-lsp lsp="(method) ProcedureBuilder<object, Meta, object, UnsetMarker, UnsetMarker, UnsetMarker, UnsetMarker, true>.use<{
    user: User | null;
}>(fn: MiddlewareBuilder<{}, Meta, {
    user: User | null;
}, UnsetMarker> | MiddlewareFunction<object, Meta, object, {
    user: User | null;
}, UnsetMarker>): ProcedureBuilder<object, Meta, {
    user: User | null;
}, UnsetMarker, UnsetMarker, UnsetMarker, UnsetMarker, true>">use</data-lsp></span><span style="color: #C9D1D9">(</span><span style="color: #FF7B72">async</span><span style="color: #C9D1D9"> (</span><span style="color: #FFA657"><data-lsp lsp="(parameter) opts: {
    ctx: {};
    type: ProcedureType;
    path: string;
    input: UnsetMarker;
    getRawInput: GetRawInputFn;
    meta: Meta | undefined;
    signal: AbortSignal | undefined;
    batchIndex: number;
    next: {
        (): Promise<MiddlewareResult<object>>;
        <$ContextOverride>(opts: {
            ctx?: $ContextOverride | undefined;
            input?: unknown;
        }): Promise<MiddlewareResult<$ContextOverride>>;
        (opts: {
            getRawInput: GetRawInputFn;
        }): Promise<...>;
    };
}">opts</data-lsp></span><span style="color: #C9D1D9">) </span><span style="color: #FF7B72">=&gt;</span><span style="color: #C9D1D9"> {</span></div><div class="line"><span style="color: #C9D1D9">    </span><span style="color: #8B949E">// Inject user into context</span></div><div class="line"><span style="color: #C9D1D9">    </span><span style="color: #FF7B72">const</span><span style="color: #C9D1D9"> </span><span style="color: #79C0FF"><data-lsp lsp="const user: User | null" style="border-bottom: solid 2px lightgrey;">user</data-lsp></span><span style="color: #C9D1D9"> </span><span style="color: #FF7B72">=</span><span style="color: #C9D1D9"> </span><span style="color: #FF7B72">await</span><span style="color: #C9D1D9"> </span><span style="color: #D2A8FF"><data-lsp lsp="(alias) currentUser(): Promise<User | null>
import currentUser">currentUser</data-lsp></span><span style="color: #C9D1D9">();</span></div><div class="meta-line"><span class="popover-prefix">           </span><span class="popover"><div class="arrow"></div>const user: User | null</span></div><div class="line"><span style="color: #C9D1D9">    </span><span style="color: #FF7B72">return</span><span style="color: #C9D1D9"> <data-lsp lsp="(parameter) opts: {
    ctx: {};
    type: ProcedureType;
    path: string;
    input: UnsetMarker;
    getRawInput: GetRawInputFn;
    meta: Meta | undefined;
    signal: AbortSignal | undefined;
    batchIndex: number;
    next: {
        (): Promise<MiddlewareResult<object>>;
        <$ContextOverride>(opts: {
            ctx?: $ContextOverride | undefined;
            input?: unknown;
        }): Promise<MiddlewareResult<$ContextOverride>>;
        (opts: {
            getRawInput: GetRawInputFn;
        }): Promise<...>;
    };
}">opts</data-lsp>.</span><span style="color: #D2A8FF"><data-lsp lsp="(property) next: <{
    user: User | null;
}>(opts: {
    ctx?: {
        user: User | null;
    } | undefined;
    input?: unknown;
}) => Promise<MiddlewareResult<{
    user: User | null;
}>> (+2 overloads)">next</data-lsp></span><span style="color: #C9D1D9">({ <data-lsp lsp="(property) ctx?: {
    user: User | null;
} | undefined">ctx</data-lsp>: { <data-lsp lsp="(property) user: User | null">user</data-lsp> } });</span></div><div class="line"><span style="color: #C9D1D9">  });</span></div><div class="line">&nbsp;</div><div class="line"><span style="color: #FF7B72">export</span><span style="color: #C9D1D9"> </span><span style="color: #FF7B72">const</span><span style="color: #C9D1D9"> </span><span style="color: #79C0FF"><data-lsp lsp="const protectedAction: ProcedureBuilder<object, Meta, {
    user: User;
}, UnsetMarker, UnsetMarker, UnsetMarker, UnsetMarker, true>">protectedAction</data-lsp></span><span style="color: #C9D1D9"> </span><span style="color: #FF7B72">=</span><span style="color: #C9D1D9"> <data-lsp lsp="const serverActionProcedure: ProcedureBuilder<object, Meta, {
    user: User | null;
}, UnsetMarker, UnsetMarker, UnsetMarker, UnsetMarker, true>">serverActionProcedure</data-lsp>.</span><span style="color: #D2A8FF"><data-lsp lsp="(method) ProcedureBuilder<object, Meta, { user: User | null; }, UnsetMarker, UnsetMarker, UnsetMarker, UnsetMarker, true>.use<{
    user: User;
}>(fn: MiddlewareBuilder<{
    user: User | null;
}, Meta, {
    user: User;
}, UnsetMarker> | MiddlewareFunction<object, Meta, {
    user: User | null;
}, {
    user: User;
}, UnsetMarker>): ProcedureBuilder<object, Meta, {
    user: User;
}, UnsetMarker, UnsetMarker, UnsetMarker, UnsetMarker, true>">use</data-lsp></span><span style="color: #C9D1D9">((</span><span style="color: #FFA657"><data-lsp lsp="(parameter) opts: {
    ctx: {
        user: User | null;
    };
    type: ProcedureType;
    path: string;
    input: UnsetMarker;
    getRawInput: GetRawInputFn;
    meta: Meta | undefined;
    signal: AbortSignal | undefined;
    batchIndex: number;
    next: {
        (): Promise<MiddlewareResult<{
            user: User | null;
        }>>;
        <$ContextOverride>(opts: {
            ctx?: $ContextOverride | undefined;
            input?: unknown;
        }): Promise<MiddlewareResult<$ContextOverride>>;
        (opts: {
            getRawInput: GetRawInputFn;
        }): Promise<...>;
    };
}">opts</data-lsp></span><span style="color: #C9D1D9">) </span><span style="color: #FF7B72">=&gt;</span><span style="color: #C9D1D9"> {</span></div><div class="line"><span style="color: #C9D1D9">  </span><span style="color: #FF7B72">if</span><span style="color: #C9D1D9"> (</span><span style="color: #FF7B72">!</span><span style="color: #C9D1D9"><data-lsp lsp="(parameter) opts: {
    ctx: {
        user: User | null;
    };
    type: ProcedureType;
    path: string;
    input: UnsetMarker;
    getRawInput: GetRawInputFn;
    meta: Meta | undefined;
    signal: AbortSignal | undefined;
    batchIndex: number;
    next: {
        (): Promise<MiddlewareResult<{
            user: User | null;
        }>>;
        <$ContextOverride>(opts: {
            ctx?: $ContextOverride | undefined;
            input?: unknown;
        }): Promise<MiddlewareResult<$ContextOverride>>;
        (opts: {
            getRawInput: GetRawInputFn;
        }): Promise<...>;
    };
}">opts</data-lsp>.<data-lsp lsp="(property) ctx: {
    user: User | null;
}">ctx</data-lsp>.<data-lsp lsp="(property) user: User | null">user</data-lsp>) {</span></div><div class="line"><span style="color: #C9D1D9">    </span><span style="color: #FF7B72">throw</span><span style="color: #C9D1D9"> </span><span style="color: #FF7B72">new</span><span style="color: #C9D1D9"> </span><span style="color: #D2A8FF"><data-lsp lsp="(alias) new TRPCError(opts: {
    message?: string;
    code: TRPC_ERROR_CODE_KEY;
    cause?: unknown;
}): TRPCError
import TRPCError">TRPCError</data-lsp></span><span style="color: #C9D1D9">({</span></div><div class="line"><span style="color: #C9D1D9">      <data-lsp lsp="(property) code: &quot;UNAUTHORIZED&quot; | &quot;PARSE_ERROR&quot; | &quot;BAD_REQUEST&quot; | &quot;INTERNAL_SERVER_ERROR&quot; | &quot;NOT_IMPLEMENTED&quot; | &quot;BAD_GATEWAY&quot; | &quot;SERVICE_UNAVAILABLE&quot; | &quot;GATEWAY_TIMEOUT&quot; | &quot;PAYMENT_REQUIRED&quot; | &quot;FORBIDDEN&quot; | &quot;NOT_FOUND&quot; | &quot;METHOD_NOT_SUPPORTED&quot; | &quot;TIMEOUT&quot; | &quot;CONFLICT&quot; | &quot;PRECONDITION_FAILED&quot; | &quot;PAYLOAD_TOO_LARGE&quot; | &quot;UNSUPPORTED_MEDIA_TYPE&quot; | &quot;UNPROCESSABLE_CONTENT&quot; | &quot;PRECONDITION_REQUIRED&quot; | &quot;TOO_MANY_REQUESTS&quot; | &quot;CLIENT_CLOSED_REQUEST&quot;">code</data-lsp>: </span><span style="color: #A5D6FF">'UNAUTHORIZED'</span><span style="color: #C9D1D9">,</span></div><div class="line"><span style="color: #C9D1D9">    });</span></div><div class="line"><span style="color: #C9D1D9">  }</span></div><div class="line">&nbsp;</div><div class="line"><span style="color: #C9D1D9">  </span><span style="color: #FF7B72">return</span><span style="color: #C9D1D9"> <data-lsp lsp="(parameter) opts: {
    ctx: {
        user: User | null;
    };
    type: ProcedureType;
    path: string;
    input: UnsetMarker;
    getRawInput: GetRawInputFn;
    meta: Meta | undefined;
    signal: AbortSignal | undefined;
    batchIndex: number;
    next: {
        (): Promise<MiddlewareResult<{
            user: User | null;
        }>>;
        <$ContextOverride>(opts: {
            ctx?: $ContextOverride | undefined;
            input?: unknown;
        }): Promise<MiddlewareResult<$ContextOverride>>;
        (opts: {
            getRawInput: GetRawInputFn;
        }): Promise<...>;
    };
}">opts</data-lsp>.</span><span style="color: #D2A8FF"><data-lsp lsp="(property) next: <{
    user: User;
}>(opts: {
    ctx?: {
        user: User;
    } | undefined;
    input?: unknown;
}) => Promise<MiddlewareResult<{
    user: User;
}>> (+2 overloads)">next</data-lsp></span><span style="color: #C9D1D9">({</span></div><div class="line"><span style="color: #C9D1D9">    <data-lsp lsp="(property) ctx?: {
    user: User;
} | undefined">ctx</data-lsp>: {</span></div><div class="line"><span style="color: #C9D1D9">      </span><span style="color: #FF7B72">...</span><span style="color: #C9D1D9"><data-lsp lsp="(parameter) opts: {
    ctx: {
        user: User | null;
    };
    type: ProcedureType;
    path: string;
    input: UnsetMarker;
    getRawInput: GetRawInputFn;
    meta: Meta | undefined;
    signal: AbortSignal | undefined;
    batchIndex: number;
    next: {
        (): Promise<MiddlewareResult<{
            user: User | null;
        }>>;
        <$ContextOverride>(opts: {
            ctx?: $ContextOverride | undefined;
            input?: unknown;
        }): Promise<MiddlewareResult<$ContextOverride>>;
        (opts: {
            getRawInput: GetRawInputFn;
        }): Promise<...>;
    };
}">opts</data-lsp>.<data-lsp lsp="(property) ctx: {
    user: User | null;
}">ctx</data-lsp>,</span></div><div class="line"><span style="color: #C9D1D9">      <data-lsp lsp="(property) user: User" style="border-bottom: solid 2px lightgrey;">user</data-lsp>: <data-lsp lsp="(parameter) opts: {
    ctx: {
        user: User | null;
    };
    type: ProcedureType;
    path: string;
    input: UnsetMarker;
    getRawInput: GetRawInputFn;
    meta: Meta | undefined;
    signal: AbortSignal | undefined;
    batchIndex: number;
    next: {
        (): Promise<MiddlewareResult<{
            user: User | null;
        }>>;
        <$ContextOverride>(opts: {
            ctx?: $ContextOverride | undefined;
            input?: unknown;
        }): Promise<MiddlewareResult<$ContextOverride>>;
        (opts: {
            getRawInput: GetRawInputFn;
        }): Promise<...>;
    };
}">opts</data-lsp>.<data-lsp lsp="(property) ctx: {
    user: User | null;
}">ctx</data-lsp>.<data-lsp lsp="(property) user: User" style="border-bottom: solid 2px lightgrey;">user</data-lsp>, </span><span style="color: #8B949E">// &lt;-- ensures type is non-nullable</span></div><div class="meta-line"><span class="popover-prefix">       </span><span class="popover"><div class="arrow"></div>(property) user: User</span></div><div class="line"><span style="color: #C9D1D9">    },</span></div><div class="line"><span style="color: #C9D1D9">  });</span></div><div class="line"><span style="color: #C9D1D9">});</span></div></code></div><button type="button" aria-label="Copy code to clipboard" class="copy-button" onclick="navigator.clipboard.writeText(this.previousSibling.innerText),this.classList.add(&quot;copied&quot;),this.textContent=&quot;Copied&quot;,setTimeout(()=>{this.classList.remove(&quot;copied&quot;),this.textContent=&quot;Copy&quot;},2e3)">Copy</button></pre></div>
<p>Okej, låt oss skriva en riktig serveråtgärd. Skapa en <code>_actions.ts</code>-fil, dekorera den med direktivet <code>"use server"</code>, och definiera din åtgärd.</p>
<div><pre class="shiki min-light with-title twoslash lsp" style="background-color: #ffffff; color: #24292eff" title="app/_actions.ts"><div class="code-title">app/_actions.ts</div><div class="language-id">ts</div><div class="code-container"><code><div class="line"><span style="color: #22863A">'use server'</span><span style="color: #24292EFF">;</span></div><div class="line">&nbsp;</div><div class="line"><span style="color: #D32F2F">import</span><span style="color: #24292EFF"> { <data-lsp lsp="import z">z</data-lsp> } </span><span style="color: #D32F2F">from</span><span style="color: #24292EFF"> </span><span style="color: #22863A">'zod'</span><span style="color: #24292EFF">;</span></div><div class="line"><span style="color: #D32F2F">import</span><span style="color: #24292EFF"> { <data-lsp lsp="(alias) const protectedAction: ProcedureBuilder<object, Meta, {
    user: User;
}, UnsetMarker, UnsetMarker, UnsetMarker, UnsetMarker, true>
import protectedAction">protectedAction</data-lsp> } </span><span style="color: #D32F2F">from</span><span style="color: #24292EFF"> </span><span style="color: #22863A">'../server/trpc'</span><span style="color: #24292EFF">;</span></div><div class="line">&nbsp;</div><div class="line"><span style="color: #D32F2F">export</span><span style="color: #24292EFF"> </span><span style="color: #D32F2F">const</span><span style="color: #24292EFF"> </span><span style="color: #1976D2"><data-lsp lsp="const createPost: (input: {
    title: string;
}) => Promise<void>">createPost</data-lsp></span><span style="color: #24292EFF"> </span><span style="color: #D32F2F">=</span><span style="color: #24292EFF"> <data-lsp lsp="(alias) const protectedAction: ProcedureBuilder<object, Meta, {
    user: User;
}, UnsetMarker, UnsetMarker, UnsetMarker, UnsetMarker, true>
import protectedAction">protectedAction</data-lsp></span></div><div class="line"><span style="color: #24292EFF">  </span><span style="color: #6F42C1">.<data-lsp lsp="(method) ProcedureBuilder<object, Meta, { user: User; }, UnsetMarker, UnsetMarker, UnsetMarker, UnsetMarker, true>.input<z.ZodObject<{
    title: z.ZodString;
}, z.core.$strip>>(schema: z.ZodObject<{
    title: z.ZodString;
}, z.core.$strip>): ProcedureBuilder<object, Meta, {
    user: User;
}, {
    title: string;
}, {
    title: string;
}, UnsetMarker, UnsetMarker, true>">input</data-lsp></span><span style="color: #24292EFF">(</span></div><div class="line"><span style="color: #24292EFF">    </span><span style="color: #1976D2"><data-lsp lsp="import z">z</data-lsp></span><span style="color: #6F42C1">.<data-lsp lsp="function object<{
    title: z.ZodString;
}>(shape?: {
    title: z.ZodString;
} | undefined, params?: string | {
    error?: string | z.core.$ZodErrorMap<NonNullable<z.core.$ZodIssueUnrecognizedKeys | z.core.$ZodIssueInvalidType<unknown>>> | undefined;
    message?: string | undefined | undefined;
} | undefined): z.ZodObject<{
    title: z.ZodString;
}, z.core.$strip>">object</data-lsp></span><span style="color: #24292EFF">({</span></div><div class="line"><span style="color: #24292EFF">      <data-lsp lsp="(property) title: z.ZodString">title</data-lsp></span><span style="color: #D32F2F">:</span><span style="color: #24292EFF"> </span><span style="color: #1976D2"><data-lsp lsp="import z">z</data-lsp></span><span style="color: #6F42C1">.<data-lsp lsp="function string(params?: string | z.core.$ZodStringParams): z.ZodString (+1 overload)">string</data-lsp></span><span style="color: #24292EFF">()</span><span style="color: #212121">,</span></div><div class="line"><span style="color: #24292EFF">    })</span><span style="color: #212121">,</span></div><div class="line"><span style="color: #24292EFF">  )</span></div><div class="line"><span style="color: #24292EFF">  </span><span style="color: #6F42C1">.<data-lsp lsp="(method) ProcedureBuilder<object, Meta, { user: User; }, { title: string; }, { title: string; }, UnsetMarker, UnsetMarker, true>.mutation<void>(resolver: ProcedureResolver<object, Meta, {
    user: User;
}, {
    title: string;
}, UnsetMarker, void>): (input: {
    title: string;
}) => Promise<void>">mutation</data-lsp></span><span style="color: #24292EFF">(</span><span style="color: #D32F2F">async</span><span style="color: #24292EFF"> (<data-lsp lsp="(parameter) opts: ProcedureResolverOptions<object, Meta, {
    user: User;
}, {
    title: string;
}>">opts</data-lsp>) </span><span style="color: #D32F2F">=&gt;</span><span style="color: #24292EFF"> {</span></div><div class="line"><span style="color: #24292EFF">    </span><span style="color: #C2C3C5">// Do something with the input</span></div><div class="line"><span style="color: #24292EFF">  });</span></div><div class="line">&nbsp;</div><div class="line"><span style="color: #C2C3C5">// Since we're using the `experimental_caller`,</span></div><div class="line"><span style="color: #C2C3C5">// our procedure is now just an ordinary function:</span></div><div class="line"><span style="color: #24292EFF"><data-lsp lsp="const createPost: (input: {
    title: string;
}) => Promise<void>" style="border-bottom: solid 2px lightgrey;">createPost</data-lsp>;</span></div><div class="meta-line"><span class="popover-prefix">    </span><span class="popover"><div class="arrow"></div>const createPost: (input: {
    title: string;
}) =&gt; Promise&lt;void&gt;</span></div></code></div><button type="button" aria-label="Copy code to clipboard" class="copy-button" onclick="navigator.clipboard.writeText(this.previousSibling.innerText),this.classList.add(&quot;copied&quot;),this.textContent=&quot;Copied&quot;,setTimeout(()=>{this.classList.remove(&quot;copied&quot;),this.textContent=&quot;Copy&quot;},2e3)">Copy</button></pre>
<pre class="shiki github-dark with-title twoslash lsp" style="background-color: #0d1117; color: #c9d1d9" title="app/_actions.ts"><div class="code-title">app/_actions.ts</div><div class="language-id">ts</div><div class="code-container"><code><div class="line"><span style="color: #A5D6FF">'use server'</span><span style="color: #C9D1D9">;</span></div><div class="line">&nbsp;</div><div class="line"><span style="color: #FF7B72">import</span><span style="color: #C9D1D9"> { <data-lsp lsp="import z">z</data-lsp> } </span><span style="color: #FF7B72">from</span><span style="color: #C9D1D9"> </span><span style="color: #A5D6FF">'zod'</span><span style="color: #C9D1D9">;</span></div><div class="line"><span style="color: #FF7B72">import</span><span style="color: #C9D1D9"> { <data-lsp lsp="(alias) const protectedAction: ProcedureBuilder<object, Meta, {
    user: User;
}, UnsetMarker, UnsetMarker, UnsetMarker, UnsetMarker, true>
import protectedAction">protectedAction</data-lsp> } </span><span style="color: #FF7B72">from</span><span style="color: #C9D1D9"> </span><span style="color: #A5D6FF">'../server/trpc'</span><span style="color: #C9D1D9">;</span></div><div class="line">&nbsp;</div><div class="line"><span style="color: #FF7B72">export</span><span style="color: #C9D1D9"> </span><span style="color: #FF7B72">const</span><span style="color: #C9D1D9"> </span><span style="color: #79C0FF"><data-lsp lsp="const createPost: (input: {
    title: string;
}) => Promise<void>">createPost</data-lsp></span><span style="color: #C9D1D9"> </span><span style="color: #FF7B72">=</span><span style="color: #C9D1D9"> <data-lsp lsp="(alias) const protectedAction: ProcedureBuilder<object, Meta, {
    user: User;
}, UnsetMarker, UnsetMarker, UnsetMarker, UnsetMarker, true>
import protectedAction">protectedAction</data-lsp></span></div><div class="line"><span style="color: #C9D1D9">  .</span><span style="color: #D2A8FF"><data-lsp lsp="(method) ProcedureBuilder<object, Meta, { user: User; }, UnsetMarker, UnsetMarker, UnsetMarker, UnsetMarker, true>.input<z.ZodObject<{
    title: z.ZodString;
}, z.core.$strip>>(schema: z.ZodObject<{
    title: z.ZodString;
}, z.core.$strip>): ProcedureBuilder<object, Meta, {
    user: User;
}, {
    title: string;
}, {
    title: string;
}, UnsetMarker, UnsetMarker, true>">input</data-lsp></span><span style="color: #C9D1D9">(</span></div><div class="line"><span style="color: #C9D1D9">    <data-lsp lsp="import z">z</data-lsp>.</span><span style="color: #D2A8FF"><data-lsp lsp="function object<{
    title: z.ZodString;
}>(shape?: {
    title: z.ZodString;
} | undefined, params?: string | {
    error?: string | z.core.$ZodErrorMap<NonNullable<z.core.$ZodIssueUnrecognizedKeys | z.core.$ZodIssueInvalidType<unknown>>> | undefined;
    message?: string | undefined | undefined;
} | undefined): z.ZodObject<{
    title: z.ZodString;
}, z.core.$strip>">object</data-lsp></span><span style="color: #C9D1D9">({</span></div><div class="line"><span style="color: #C9D1D9">      <data-lsp lsp="(property) title: z.ZodString">title</data-lsp>: <data-lsp lsp="import z">z</data-lsp>.</span><span style="color: #D2A8FF"><data-lsp lsp="function string(params?: string | z.core.$ZodStringParams): z.ZodString (+1 overload)">string</data-lsp></span><span style="color: #C9D1D9">(),</span></div><div class="line"><span style="color: #C9D1D9">    }),</span></div><div class="line"><span style="color: #C9D1D9">  )</span></div><div class="line"><span style="color: #C9D1D9">  .</span><span style="color: #D2A8FF"><data-lsp lsp="(method) ProcedureBuilder<object, Meta, { user: User; }, { title: string; }, { title: string; }, UnsetMarker, UnsetMarker, true>.mutation<void>(resolver: ProcedureResolver<object, Meta, {
    user: User;
}, {
    title: string;
}, UnsetMarker, void>): (input: {
    title: string;
}) => Promise<void>">mutation</data-lsp></span><span style="color: #C9D1D9">(</span><span style="color: #FF7B72">async</span><span style="color: #C9D1D9"> (</span><span style="color: #FFA657"><data-lsp lsp="(parameter) opts: ProcedureResolverOptions<object, Meta, {
    user: User;
}, {
    title: string;
}>">opts</data-lsp></span><span style="color: #C9D1D9">) </span><span style="color: #FF7B72">=&gt;</span><span style="color: #C9D1D9"> {</span></div><div class="line"><span style="color: #C9D1D9">    </span><span style="color: #8B949E">// Do something with the input</span></div><div class="line"><span style="color: #C9D1D9">  });</span></div><div class="line">&nbsp;</div><div class="line"><span style="color: #8B949E">// Since we're using the `experimental_caller`,</span></div><div class="line"><span style="color: #8B949E">// our procedure is now just an ordinary function:</span></div><div class="line"><span style="color: #C9D1D9"><data-lsp lsp="const createPost: (input: {
    title: string;
}) => Promise<void>" style="border-bottom: solid 2px lightgrey;">createPost</data-lsp>;</span></div><div class="meta-line"><span class="popover-prefix">    </span><span class="popover"><div class="arrow"></div>const createPost: (input: {
    title: string;
}) =&gt; Promise&lt;void&gt;</span></div></code></div><button type="button" aria-label="Copy code to clipboard" class="copy-button" onclick="navigator.clipboard.writeText(this.previousSibling.innerText),this.classList.add(&quot;copied&quot;),this.textContent=&quot;Copied&quot;,setTimeout(()=>{this.classList.remove(&quot;copied&quot;),this.textContent=&quot;Copy&quot;},2e3)">Copy</button></pre></div>
<p>Wow, så enkelt är det att definiera en serveråtgärd som är skyddad från oautentiserade användare, med indatavalidering för att skydda mot attacker som SQL-injektioner. Låt oss importera den här funktionen på klienten och anropa den.</p>
<div><pre class="shiki min-light with-title" style="background-color: #ffffff; color: #24292eff" title="app/post-form.tsx"><div class="code-title">app/post-form.tsx</div><div class="language-id">tsx</div><div class="code-container"><code><div class="line"><span style="color: #22863A">'use client'</span><span style="color: #24292EFF">;</span></div><div class="line"></div><div class="line"><span style="color: #D32F2F">import</span><span style="color: #24292EFF"> { createPost } </span><span style="color: #D32F2F">from</span><span style="color: #24292EFF"> </span><span style="color: #22863A">'./_actions'</span><span style="color: #24292EFF">;</span></div><div class="line"></div><div class="line"><span style="color: #D32F2F">export</span><span style="color: #24292EFF"> </span><span style="color: #D32F2F">function</span><span style="color: #24292EFF"> </span><span style="color: #6F42C1">PostForm</span><span style="color: #24292EFF">() {</span></div><div class="line"><span style="color: #24292EFF">  </span><span style="color: #D32F2F">return</span><span style="color: #24292EFF"> (</span></div><div class="line"><span style="color: #24292EFF">    &lt;</span><span style="color: #22863A">form</span></div><div class="line"><span style="color: #24292EFF">      </span><span style="color: #C2C3C5">// Use `action` to make form progressively enhanced</span></div><div class="line"><span style="color: #24292EFF">      </span><span style="color: #6F42C1">action</span><span style="color: #D32F2F">=</span><span style="color: #24292EFF">{createPost}</span></div><div class="line"><span style="color: #24292EFF">      </span><span style="color: #C2C3C5">// `Using `onSubmit` allows building rich interactive</span></div><div class="line"><span style="color: #24292EFF">      </span><span style="color: #C2C3C5">// forms once JavaScript has loaded</span></div><div class="line"><span style="color: #24292EFF">      </span><span style="color: #6F42C1">onSubmit</span><span style="color: #D32F2F">=</span><span style="color: #24292EFF">{</span><span style="color: #D32F2F">async</span><span style="color: #24292EFF"> (e) </span><span style="color: #D32F2F">=&gt;</span><span style="color: #24292EFF"> {</span></div><div class="line"><span style="color: #24292EFF">        </span><span style="color: #1976D2">e</span><span style="color: #6F42C1">.preventDefault</span><span style="color: #24292EFF">();</span></div><div class="line"><span style="color: #24292EFF">        </span><span style="color: #D32F2F">const</span><span style="color: #24292EFF"> </span><span style="color: #1976D2">title</span><span style="color: #24292EFF"> </span><span style="color: #D32F2F">=</span><span style="color: #24292EFF"> </span><span style="color: #D32F2F">new</span><span style="color: #24292EFF"> </span><span style="color: #6F42C1">FormData</span><span style="color: #24292EFF">(</span><span style="color: #1976D2">e</span><span style="color: #24292EFF">.currentTarget)</span><span style="color: #6F42C1">.get</span><span style="color: #24292EFF">(</span><span style="color: #22863A">'title'</span><span style="color: #24292EFF">) </span><span style="color: #D32F2F">as</span><span style="color: #24292EFF"> </span><span style="color: #1976D2">string</span><span style="color: #24292EFF">;</span></div><div class="line"><span style="color: #24292EFF">        </span><span style="color: #C2C3C5">// Maybe show loading toast, etc etc. Endless possibilities</span></div><div class="line"><span style="color: #24292EFF">        </span><span style="color: #D32F2F">await</span><span style="color: #24292EFF"> </span><span style="color: #6F42C1">createPost</span><span style="color: #24292EFF">({ title });</span></div><div class="line"><span style="color: #24292EFF">      }}</span></div><div class="line"><span style="color: #24292EFF">    &gt;</span></div><div class="line"><span style="color: #24292EFF">      &lt;</span><span style="color: #22863A">input</span><span style="color: #24292EFF"> </span><span style="color: #6F42C1">type</span><span style="color: #D32F2F">=</span><span style="color: #22863A">"text"</span><span style="color: #24292EFF"> </span><span style="color: #6F42C1">name</span><span style="color: #D32F2F">=</span><span style="color: #22863A">"title"</span><span style="color: #24292EFF"> /&gt;</span></div><div class="line"><span style="color: #24292EFF">      &lt;</span><span style="color: #22863A">button</span><span style="color: #24292EFF"> </span><span style="color: #6F42C1">type</span><span style="color: #D32F2F">=</span><span style="color: #22863A">"submit"</span><span style="color: #24292EFF">&gt;Create Post&lt;/</span><span style="color: #22863A">button</span><span style="color: #24292EFF">&gt;</span></div><div class="line"><span style="color: #24292EFF">    &lt;/</span><span style="color: #22863A">form</span><span style="color: #24292EFF">&gt;</span></div><div class="line"><span style="color: #24292EFF">  );</span></div><div class="line"><span style="color: #24292EFF">}</span></div></code></div><button type="button" aria-label="Copy code to clipboard" class="copy-button" onclick="navigator.clipboard.writeText(this.previousSibling.innerText),this.classList.add(&quot;copied&quot;),this.textContent=&quot;Copied&quot;,setTimeout(()=>{this.classList.remove(&quot;copied&quot;),this.textContent=&quot;Copy&quot;},2e3)">Copy</button></pre>
<pre class="shiki github-dark with-title" style="background-color: #0d1117; color: #c9d1d9" title="app/post-form.tsx"><div class="code-title">app/post-form.tsx</div><div class="language-id">tsx</div><div class="code-container"><code><div class="line"><span style="color: #A5D6FF">'use client'</span><span style="color: #C9D1D9">;</span></div><div class="line"></div><div class="line"><span style="color: #FF7B72">import</span><span style="color: #C9D1D9"> { createPost } </span><span style="color: #FF7B72">from</span><span style="color: #C9D1D9"> </span><span style="color: #A5D6FF">'./_actions'</span><span style="color: #C9D1D9">;</span></div><div class="line"></div><div class="line"><span style="color: #FF7B72">export</span><span style="color: #C9D1D9"> </span><span style="color: #FF7B72">function</span><span style="color: #C9D1D9"> </span><span style="color: #D2A8FF">PostForm</span><span style="color: #C9D1D9">() {</span></div><div class="line"><span style="color: #C9D1D9">  </span><span style="color: #FF7B72">return</span><span style="color: #C9D1D9"> (</span></div><div class="line"><span style="color: #C9D1D9">    &lt;</span><span style="color: #7EE787">form</span></div><div class="line"><span style="color: #C9D1D9">      </span><span style="color: #8B949E">// Use `action` to make form progressively enhanced</span></div><div class="line"><span style="color: #C9D1D9">      </span><span style="color: #79C0FF">action</span><span style="color: #FF7B72">=</span><span style="color: #C9D1D9">{createPost}</span></div><div class="line"><span style="color: #C9D1D9">      </span><span style="color: #8B949E">// `Using `onSubmit` allows building rich interactive</span></div><div class="line"><span style="color: #C9D1D9">      </span><span style="color: #8B949E">// forms once JavaScript has loaded</span></div><div class="line"><span style="color: #C9D1D9">      </span><span style="color: #79C0FF">onSubmit</span><span style="color: #FF7B72">=</span><span style="color: #C9D1D9">{</span><span style="color: #FF7B72">async</span><span style="color: #C9D1D9"> (</span><span style="color: #FFA657">e</span><span style="color: #C9D1D9">) </span><span style="color: #FF7B72">=&gt;</span><span style="color: #C9D1D9"> {</span></div><div class="line"><span style="color: #C9D1D9">        e.</span><span style="color: #D2A8FF">preventDefault</span><span style="color: #C9D1D9">();</span></div><div class="line"><span style="color: #C9D1D9">        </span><span style="color: #FF7B72">const</span><span style="color: #C9D1D9"> </span><span style="color: #79C0FF">title</span><span style="color: #C9D1D9"> </span><span style="color: #FF7B72">=</span><span style="color: #C9D1D9"> </span><span style="color: #FF7B72">new</span><span style="color: #C9D1D9"> </span><span style="color: #D2A8FF">FormData</span><span style="color: #C9D1D9">(e.currentTarget).</span><span style="color: #D2A8FF">get</span><span style="color: #C9D1D9">(</span><span style="color: #A5D6FF">'title'</span><span style="color: #C9D1D9">) </span><span style="color: #FF7B72">as</span><span style="color: #C9D1D9"> </span><span style="color: #79C0FF">string</span><span style="color: #C9D1D9">;</span></div><div class="line"><span style="color: #C9D1D9">        </span><span style="color: #8B949E">// Maybe show loading toast, etc etc. Endless possibilities</span></div><div class="line"><span style="color: #C9D1D9">        </span><span style="color: #FF7B72">await</span><span style="color: #C9D1D9"> </span><span style="color: #D2A8FF">createPost</span><span style="color: #C9D1D9">({ title });</span></div><div class="line"><span style="color: #C9D1D9">      }}</span></div><div class="line"><span style="color: #C9D1D9">    &gt;</span></div><div class="line"><span style="color: #C9D1D9">      &lt;</span><span style="color: #7EE787">input</span><span style="color: #C9D1D9"> </span><span style="color: #79C0FF">type</span><span style="color: #FF7B72">=</span><span style="color: #A5D6FF">"text"</span><span style="color: #C9D1D9"> </span><span style="color: #79C0FF">name</span><span style="color: #FF7B72">=</span><span style="color: #A5D6FF">"title"</span><span style="color: #C9D1D9"> /&gt;</span></div><div class="line"><span style="color: #C9D1D9">      &lt;</span><span style="color: #7EE787">button</span><span style="color: #C9D1D9"> </span><span style="color: #79C0FF">type</span><span style="color: #FF7B72">=</span><span style="color: #A5D6FF">"submit"</span><span style="color: #C9D1D9">&gt;Create Post&lt;/</span><span style="color: #7EE787">button</span><span style="color: #C9D1D9">&gt;</span></div><div class="line"><span style="color: #C9D1D9">    &lt;/</span><span style="color: #7EE787">form</span><span style="color: #C9D1D9">&gt;</span></div><div class="line"><span style="color: #C9D1D9">  );</span></div><div class="line"><span style="color: #C9D1D9">}</span></div></code></div><button type="button" aria-label="Copy code to clipboard" class="copy-button" onclick="navigator.clipboard.writeText(this.previousSibling.innerText),this.classList.add(&quot;copied&quot;),this.textContent=&quot;Copied&quot;,setTimeout(()=>{this.classList.remove(&quot;copied&quot;),this.textContent=&quot;Copy&quot;},2e3)">Copy</button></pre></div>
<h2 class="anchor anchorWithStickyNavbar_MYIC" id="vidareutveckling">Vidareutveckling<a href="https://trpc.io/sv/blog/trpc-actions#vidareutveckling" class="hash-link" aria-label="Direktlänk till Vidareutveckling" title="Direktlänk till Vidareutveckling">​</a></h2>
<p>Genom att använda tRPC<!-- -->:s<!-- --> byggstenar och dess komponerbara sätt att definiera återanvändbara procedurer kan vi enkelt skapa mer komplexa serveråtgärder. Här är några exempel:</p>
<h3 class="anchor anchorWithStickyNavbar_MYIC" id="observabilitet">Observabilitet<a href="https://trpc.io/sv/blog/trpc-actions#observabilitet" class="hash-link" aria-label="Direktlänk till Observabilitet" title="Direktlänk till Observabilitet">​</a></h3>
<p>Du kan använda <code>@baselime/node-opentelemtry</code>:s<!-- --> tRPC-plugin för att lägga till observabilitet med bara några rader kod:</p>
<div><pre class="shiki min-light" style="background-color: #ffffff; color: #24292eff"><div class="language-id">diff</div><div class="code-container"><code><div class="line"><span style="color: #24292EFF">--- server/trpc.ts</span></div><div class="line"><span style="color: #24292EFF">+++ server/trpc.ts</span></div><div class="line"><span style="color: #24292EFF">+ import { tracing } from '@baselime/node-opentelemetry/trpc';</span></div><div class="line"></div><div class="line"><span style="color: #24292EFF">  export const serverActionProcedure = t.procedure</span></div><div class="line"><span style="color: #24292EFF">    .experimental_caller(</span></div><div class="line"><span style="color: #24292EFF">      experimental_nextAppDirCaller({</span></div><div class="line"><span style="color: #24292EFF">        pathExtractor: (meta: Meta) =&gt; meta.span,</span></div><div class="line"><span style="color: #24292EFF">      }),</span></div><div class="line"><span style="color: #24292EFF">    )</span></div><div class="line"><span style="color: #24292EFF">    .use(async (opts) =&gt; {</span></div><div class="line"><span style="color: #24292EFF">      // Inject user into context</span></div><div class="line"><span style="color: #24292EFF">      const user = await currentUser();</span></div><div class="line"><span style="color: #24292EFF">      return opts.next({ ctx: { user } });</span></div><div class="line"><span style="color: #24292EFF">    })</span></div><div class="line"><span style="color: #24292EFF">+  .use(tracing());</span></div><div class="line"></div><div class="line"><span style="color: #24292EFF">--- app/_actions.ts</span></div><div class="line"><span style="color: #24292EFF">+++ app/_actions.ts</span></div><div class="line"><span style="color: #24292EFF">  export const createPost = protectedAction</span></div><div class="line"><span style="color: #24292EFF">+   .meta({ span: 'create-post' })</span></div><div class="line"><span style="color: #24292EFF">    .input(</span></div><div class="line"><span style="color: #24292EFF">      z.object({</span></div><div class="line"><span style="color: #24292EFF">        title: z.string(),</span></div><div class="line"><span style="color: #24292EFF">      }),</span></div><div class="line"><span style="color: #24292EFF">    )</span></div><div class="line"><span style="color: #24292EFF">    .mutation(async (opts) =&gt; {</span></div><div class="line"><span style="color: #24292EFF">      // Do something with the input</span></div><div class="line"><span style="color: #24292EFF">    });</span></div></code></div><button type="button" aria-label="Copy code to clipboard" class="copy-button" onclick="navigator.clipboard.writeText(this.previousSibling.innerText),this.classList.add(&quot;copied&quot;),this.textContent=&quot;Copied&quot;,setTimeout(()=>{this.classList.remove(&quot;copied&quot;),this.textContent=&quot;Copy&quot;},2e3)">Copy</button></pre>
<pre class="shiki github-dark" style="background-color: #0d1117; color: #c9d1d9"><div class="language-id">diff</div><div class="code-container"><code><div class="line"><span style="color: #FFA198">--- server/trpc.ts</span></div><div class="line"><span style="color: #7EE787">+++ server/trpc.ts</span></div><div class="line"><span style="color: #7EE787">+ import { tracing } from '@baselime/node-opentelemetry/trpc';</span></div><div class="line"></div><div class="line"><span style="color: #C9D1D9">  export const serverActionProcedure = t.procedure</span></div><div class="line"><span style="color: #C9D1D9">    .experimental_caller(</span></div><div class="line"><span style="color: #C9D1D9">      experimental_nextAppDirCaller({</span></div><div class="line"><span style="color: #C9D1D9">        pathExtractor: (meta: Meta) =&gt; meta.span,</span></div><div class="line"><span style="color: #C9D1D9">      }),</span></div><div class="line"><span style="color: #C9D1D9">    )</span></div><div class="line"><span style="color: #C9D1D9">    .use(async (opts) =&gt; {</span></div><div class="line"><span style="color: #C9D1D9">      // Inject user into context</span></div><div class="line"><span style="color: #C9D1D9">      const user = await currentUser();</span></div><div class="line"><span style="color: #C9D1D9">      return opts.next({ ctx: { user } });</span></div><div class="line"><span style="color: #C9D1D9">    })</span></div><div class="line"><span style="color: #7EE787">+  .use(tracing());</span></div><div class="line"></div><div class="line"><span style="color: #FFA198">--- app/_actions.ts</span></div><div class="line"><span style="color: #7EE787">+++ app/_actions.ts</span></div><div class="line"><span style="color: #C9D1D9">  export const createPost = protectedAction</span></div><div class="line"><span style="color: #7EE787">+   .meta({ span: 'create-post' })</span></div><div class="line"><span style="color: #C9D1D9">    .input(</span></div><div class="line"><span style="color: #C9D1D9">      z.object({</span></div><div class="line"><span style="color: #C9D1D9">        title: z.string(),</span></div><div class="line"><span style="color: #C9D1D9">      }),</span></div><div class="line"><span style="color: #C9D1D9">    )</span></div><div class="line"><span style="color: #C9D1D9">    .mutation(async (opts) =&gt; {</span></div><div class="line"><span style="color: #C9D1D9">      // Do something with the input</span></div><div class="line"><span style="color: #C9D1D9">    });</span></div></code></div><button type="button" aria-label="Copy code to clipboard" class="copy-button" onclick="navigator.clipboard.writeText(this.previousSibling.innerText),this.classList.add(&quot;copied&quot;),this.textContent=&quot;Copied&quot;,setTimeout(()=>{this.classList.remove(&quot;copied&quot;),this.textContent=&quot;Copy&quot;},2e3)">Copy</button></pre></div>
<p>Kolla in <a href="https://github.com/baselime/node-opentelemetry/blob/main/TRPC.md" target="_blank" rel="noopener noreferrer">Baselimes tRPC-integrering</a> för mer information. Liknande mönster bör fungera oavsett vilken observabilitetsplattform du använder.</p>
<h3 class="anchor anchorWithStickyNavbar_MYIC" id="begränsning-av-anrop-rate-limiting">Begränsning av anrop (Rate Limiting)<a href="https://trpc.io/sv/blog/trpc-actions#begr%C3%A4nsning-av-anrop-rate-limiting" class="hash-link" aria-label="Direktlänk till Begränsning av anrop (Rate Limiting)" title="Direktlänk till Begränsning av anrop (Rate Limiting)">​</a></h3>
<p>Du kan använda en tjänst som Unkey för att begränsa antalet anrop till dina serveråtgärder. Här är ett exempel på en skyddad serveråtgärd som använder Unkey för att begränsa antalet förfrågningar per användare:</p>
<div></div>
<div><pre class="shiki min-light with-title twoslash lsp" style="background-color: #ffffff; color: #24292eff" title="server/trpc.ts"><div class="code-title">server/trpc.ts</div><div class="language-id">ts</div><div class="code-container"><code><div class="line"><span style="color: #D32F2F">import</span><span style="color: #24292EFF"> { <data-lsp lsp="(alias) class Ratelimit
import Ratelimit">Ratelimit</data-lsp> } </span><span style="color: #D32F2F">from</span><span style="color: #24292EFF"> </span><span style="color: #22863A">'@unkey/ratelimit'</span><span style="color: #24292EFF">;</span></div><div class="line">&nbsp;</div><div class="line"><span style="color: #D32F2F">export</span><span style="color: #24292EFF"> </span><span style="color: #D32F2F">const</span><span style="color: #24292EFF"> </span><span style="color: #1976D2"><data-lsp lsp="const rateLimitedAction: ProcedureBuilder<object, Meta, {
    user: User;
}, UnsetMarker, UnsetMarker, UnsetMarker, UnsetMarker, true>">rateLimitedAction</data-lsp></span><span style="color: #24292EFF"> </span><span style="color: #D32F2F">=</span><span style="color: #24292EFF"> </span><span style="color: #1976D2"><data-lsp lsp="const protectedAction: ProcedureBuilder<object, Meta, {
    user: User;
}, UnsetMarker, UnsetMarker, UnsetMarker, UnsetMarker, true>">protectedAction</data-lsp></span><span style="color: #6F42C1">.<data-lsp lsp="(method) ProcedureBuilder<object, Meta, { user: User; }, UnsetMarker, UnsetMarker, UnsetMarker, UnsetMarker, true>.use<{
    user: User;
}>(fn: MiddlewareBuilder<{
    user: User;
}, Meta, {
    user: User;
}, UnsetMarker> | MiddlewareFunction<object, Meta, {
    user: User;
}, {
    user: User;
}, UnsetMarker>): ProcedureBuilder<object, Meta, {
    user: User;
}, UnsetMarker, UnsetMarker, UnsetMarker, UnsetMarker, true>">use</data-lsp></span><span style="color: #24292EFF">(</span><span style="color: #D32F2F">async</span><span style="color: #24292EFF"> (<data-lsp lsp="(parameter) opts: {
    ctx: {
        user: User;
    };
    type: ProcedureType;
    path: string;
    input: UnsetMarker;
    getRawInput: GetRawInputFn;
    meta: Meta | undefined;
    signal: AbortSignal | undefined;
    batchIndex: number;
    next: {
        (): Promise<MiddlewareResult<{
            user: User;
        }>>;
        <$ContextOverride>(opts: {
            ctx?: $ContextOverride | undefined;
            input?: unknown;
        }): Promise<MiddlewareResult<$ContextOverride>>;
        (opts: {
            getRawInput: GetRawInputFn;
        }): Promise<...>;
    };
}">opts</data-lsp>) </span><span style="color: #D32F2F">=&gt;</span><span style="color: #24292EFF"> {</span></div><div class="line"><span style="color: #24292EFF">  </span><span style="color: #D32F2F">const</span><span style="color: #24292EFF"> </span><span style="color: #1976D2"><data-lsp lsp="const unkey: Ratelimit">unkey</data-lsp></span><span style="color: #24292EFF"> </span><span style="color: #D32F2F">=</span><span style="color: #24292EFF"> </span><span style="color: #D32F2F">new</span><span style="color: #24292EFF"> </span><span style="color: #6F42C1"><data-lsp lsp="(alias) new Ratelimit(config: RatelimitConfig): Ratelimit
import Ratelimit">Ratelimit</data-lsp></span><span style="color: #24292EFF">({</span></div><div class="line"><span style="color: #24292EFF">    <data-lsp lsp="(property) rootKey: string">rootKey</data-lsp></span><span style="color: #D32F2F">:</span><span style="color: #24292EFF"> </span><span style="color: #1976D2"><data-lsp lsp="var process: NodeJS.Process">process</data-lsp></span><span style="color: #24292EFF">.</span><span style="color: #1976D2"><data-lsp lsp="(property) NodeJS.Process.env: NodeJS.ProcessEnv">env</data-lsp></span><span style="color: #24292EFF">.</span><span style="color: #1976D2"><data-lsp lsp="string | undefined">UNKEY_ROOT_KEY</data-lsp></span><span style="color: #D32F2F">!</span><span style="color: #212121">,</span></div><div class="line"><span style="color: #24292EFF">    <data-lsp lsp="(property) async?: boolean | undefined">async</data-lsp></span><span style="color: #D32F2F">:</span><span style="color: #24292EFF"> </span><span style="color: #1976D2">true</span><span style="color: #212121">,</span></div><div class="line"><span style="color: #24292EFF">    <data-lsp lsp="(property) duration: number | Duration">duration</data-lsp></span><span style="color: #D32F2F">:</span><span style="color: #24292EFF"> </span><span style="color: #22863A">'10s'</span><span style="color: #212121">,</span></div><div class="line"><span style="color: #24292EFF">    <data-lsp lsp="(property) limit: number">limit</data-lsp></span><span style="color: #D32F2F">:</span><span style="color: #24292EFF"> </span><span style="color: #1976D2">5</span><span style="color: #212121">,</span></div><div class="line"><span style="color: #24292EFF">    <data-lsp lsp="(property) namespace: string">namespace</data-lsp></span><span style="color: #D32F2F">:</span><span style="color: #24292EFF"> </span><span style="color: #22863A">`trpc_</span><span style="color: #D32F2F">${</span><span style="color: #1976D2"><data-lsp lsp="(parameter) opts: {
    ctx: {
        user: User;
    };
    type: ProcedureType;
    path: string;
    input: UnsetMarker;
    getRawInput: GetRawInputFn;
    meta: Meta | undefined;
    signal: AbortSignal | undefined;
    batchIndex: number;
    next: {
        (): Promise<MiddlewareResult<{
            user: User;
        }>>;
        <$ContextOverride>(opts: {
            ctx?: $ContextOverride | undefined;
            input?: unknown;
        }): Promise<MiddlewareResult<$ContextOverride>>;
        (opts: {
            getRawInput: GetRawInputFn;
        }): Promise<...>;
    };
}">opts</data-lsp></span><span style="color: #24292EFF">.<data-lsp lsp="(property) path: string">path</data-lsp></span><span style="color: #D32F2F">}</span><span style="color: #22863A">`</span><span style="color: #212121">,</span></div><div class="line"><span style="color: #24292EFF">  });</span></div><div class="line">&nbsp;</div><div class="line"><span style="color: #24292EFF">  </span><span style="color: #D32F2F">const</span><span style="color: #24292EFF"> </span><span style="color: #1976D2"><data-lsp lsp="const ratelimit: RatelimitResponse">ratelimit</data-lsp></span><span style="color: #24292EFF"> </span><span style="color: #D32F2F">=</span><span style="color: #24292EFF"> </span><span style="color: #D32F2F">await</span><span style="color: #24292EFF"> </span><span style="color: #1976D2"><data-lsp lsp="const unkey: Ratelimit">unkey</data-lsp></span><span style="color: #6F42C1">.<data-lsp lsp="(method) Ratelimit.limit(identifier: string, opts?: LimitOptions): Promise<RatelimitResponse>">limit</data-lsp></span><span style="color: #24292EFF">(</span><span style="color: #1976D2"><data-lsp lsp="(parameter) opts: {
    ctx: {
        user: User;
    };
    type: ProcedureType;
    path: string;
    input: UnsetMarker;
    getRawInput: GetRawInputFn;
    meta: Meta | undefined;
    signal: AbortSignal | undefined;
    batchIndex: number;
    next: {
        (): Promise<MiddlewareResult<{
            user: User;
        }>>;
        <$ContextOverride>(opts: {
            ctx?: $ContextOverride | undefined;
            input?: unknown;
        }): Promise<MiddlewareResult<$ContextOverride>>;
        (opts: {
            getRawInput: GetRawInputFn;
        }): Promise<...>;
    };
}">opts</data-lsp></span><span style="color: #24292EFF">.</span><span style="color: #1976D2"><data-lsp lsp="(property) ctx: {
    user: User;
}">ctx</data-lsp></span><span style="color: #24292EFF">.</span><span style="color: #1976D2"><data-lsp lsp="(property) user: User">user</data-lsp></span><span style="color: #24292EFF">.<data-lsp lsp="(property) User.id: string">id</data-lsp>);</span></div><div class="line"><span style="color: #24292EFF">  </span><span style="color: #D32F2F">if</span><span style="color: #24292EFF"> (</span><span style="color: #D32F2F">!</span><span style="color: #1976D2"><data-lsp lsp="const ratelimit: RatelimitResponse">ratelimit</data-lsp></span><span style="color: #24292EFF">.<data-lsp lsp="(property) success: boolean">success</data-lsp>) {</span></div><div class="line"><span style="color: #24292EFF">    </span><span style="color: #D32F2F">throw</span><span style="color: #24292EFF"> </span><span style="color: #D32F2F">new</span><span style="color: #24292EFF"> </span><span style="color: #6F42C1"><data-lsp lsp="(alias) new TRPCError(opts: {
    message?: string;
    code: TRPC_ERROR_CODE_KEY;
    cause?: unknown;
}): TRPCError
import TRPCError">TRPCError</data-lsp></span><span style="color: #24292EFF">({</span></div><div class="line"><span style="color: #24292EFF">      <data-lsp lsp="(property) code: &quot;UNAUTHORIZED&quot; | &quot;PARSE_ERROR&quot; | &quot;BAD_REQUEST&quot; | &quot;INTERNAL_SERVER_ERROR&quot; | &quot;NOT_IMPLEMENTED&quot; | &quot;BAD_GATEWAY&quot; | &quot;SERVICE_UNAVAILABLE&quot; | &quot;GATEWAY_TIMEOUT&quot; | &quot;PAYMENT_REQUIRED&quot; | &quot;FORBIDDEN&quot; | &quot;NOT_FOUND&quot; | &quot;METHOD_NOT_SUPPORTED&quot; | &quot;TIMEOUT&quot; | &quot;CONFLICT&quot; | &quot;PRECONDITION_FAILED&quot; | &quot;PAYLOAD_TOO_LARGE&quot; | &quot;UNSUPPORTED_MEDIA_TYPE&quot; | &quot;UNPROCESSABLE_CONTENT&quot; | &quot;PRECONDITION_REQUIRED&quot; | &quot;TOO_MANY_REQUESTS&quot; | &quot;CLIENT_CLOSED_REQUEST&quot;">code</data-lsp></span><span style="color: #D32F2F">:</span><span style="color: #24292EFF"> </span><span style="color: #22863A">'TOO_MANY_REQUESTS'</span><span style="color: #212121">,</span></div><div class="line"><span style="color: #24292EFF">      <data-lsp lsp="(property) message?: string | undefined">message</data-lsp></span><span style="color: #D32F2F">:</span><span style="color: #24292EFF"> </span><span style="color: #1976D2"><data-lsp lsp="var JSON: JSON">JSON</data-lsp></span><span style="color: #6F42C1">.<data-lsp lsp="(method) JSON.stringify(value: any, replacer?: (this: any, key: string, value: any) => any, space?: string | number): string (+1 overload)">stringify</data-lsp></span><span style="color: #24292EFF">(<data-lsp lsp="const ratelimit: RatelimitResponse">ratelimit</data-lsp>)</span><span style="color: #212121">,</span></div><div class="line"><span style="color: #24292EFF">    });</span></div><div class="line"><span style="color: #24292EFF">  }</span></div><div class="line">&nbsp;</div><div class="line"><span style="color: #24292EFF">  </span><span style="color: #D32F2F">return</span><span style="color: #24292EFF"> </span><span style="color: #1976D2"><data-lsp lsp="(parameter) opts: {
    ctx: {
        user: User;
    };
    type: ProcedureType;
    path: string;
    input: UnsetMarker;
    getRawInput: GetRawInputFn;
    meta: Meta | undefined;
    signal: AbortSignal | undefined;
    batchIndex: number;
    next: {
        (): Promise<MiddlewareResult<{
            user: User;
        }>>;
        <$ContextOverride>(opts: {
            ctx?: $ContextOverride | undefined;
            input?: unknown;
        }): Promise<MiddlewareResult<$ContextOverride>>;
        (opts: {
            getRawInput: GetRawInputFn;
        }): Promise<...>;
    };
}">opts</data-lsp></span><span style="color: #6F42C1">.<data-lsp lsp="(property) next: () => Promise<MiddlewareResult<{
    user: User;
}>> (+2 overloads)">next</data-lsp></span><span style="color: #24292EFF">();</span></div><div class="line"><span style="color: #24292EFF">});</span></div></code></div><button type="button" aria-label="Copy code to clipboard" class="copy-button" onclick="navigator.clipboard.writeText(this.previousSibling.innerText),this.classList.add(&quot;copied&quot;),this.textContent=&quot;Copied&quot;,setTimeout(()=>{this.classList.remove(&quot;copied&quot;),this.textContent=&quot;Copy&quot;},2e3)">Copy</button></pre>
<pre class="shiki github-dark with-title twoslash lsp" style="background-color: #0d1117; color: #c9d1d9" title="server/trpc.ts"><div class="code-title">server/trpc.ts</div><div class="language-id">ts</div><div class="code-container"><code><div class="line"><span style="color: #FF7B72">import</span><span style="color: #C9D1D9"> { <data-lsp lsp="(alias) class Ratelimit
import Ratelimit">Ratelimit</data-lsp> } </span><span style="color: #FF7B72">from</span><span style="color: #C9D1D9"> </span><span style="color: #A5D6FF">'@unkey/ratelimit'</span><span style="color: #C9D1D9">;</span></div><div class="line">&nbsp;</div><div class="line"><span style="color: #FF7B72">export</span><span style="color: #C9D1D9"> </span><span style="color: #FF7B72">const</span><span style="color: #C9D1D9"> </span><span style="color: #79C0FF"><data-lsp lsp="const rateLimitedAction: ProcedureBuilder<object, Meta, {
    user: User;
}, UnsetMarker, UnsetMarker, UnsetMarker, UnsetMarker, true>">rateLimitedAction</data-lsp></span><span style="color: #C9D1D9"> </span><span style="color: #FF7B72">=</span><span style="color: #C9D1D9"> <data-lsp lsp="const protectedAction: ProcedureBuilder<object, Meta, {
    user: User;
}, UnsetMarker, UnsetMarker, UnsetMarker, UnsetMarker, true>">protectedAction</data-lsp>.</span><span style="color: #D2A8FF"><data-lsp lsp="(method) ProcedureBuilder<object, Meta, { user: User; }, UnsetMarker, UnsetMarker, UnsetMarker, UnsetMarker, true>.use<{
    user: User;
}>(fn: MiddlewareBuilder<{
    user: User;
}, Meta, {
    user: User;
}, UnsetMarker> | MiddlewareFunction<object, Meta, {
    user: User;
}, {
    user: User;
}, UnsetMarker>): ProcedureBuilder<object, Meta, {
    user: User;
}, UnsetMarker, UnsetMarker, UnsetMarker, UnsetMarker, true>">use</data-lsp></span><span style="color: #C9D1D9">(</span><span style="color: #FF7B72">async</span><span style="color: #C9D1D9"> (</span><span style="color: #FFA657"><data-lsp lsp="(parameter) opts: {
    ctx: {
        user: User;
    };
    type: ProcedureType;
    path: string;
    input: UnsetMarker;
    getRawInput: GetRawInputFn;
    meta: Meta | undefined;
    signal: AbortSignal | undefined;
    batchIndex: number;
    next: {
        (): Promise<MiddlewareResult<{
            user: User;
        }>>;
        <$ContextOverride>(opts: {
            ctx?: $ContextOverride | undefined;
            input?: unknown;
        }): Promise<MiddlewareResult<$ContextOverride>>;
        (opts: {
            getRawInput: GetRawInputFn;
        }): Promise<...>;
    };
}">opts</data-lsp></span><span style="color: #C9D1D9">) </span><span style="color: #FF7B72">=&gt;</span><span style="color: #C9D1D9"> {</span></div><div class="line"><span style="color: #C9D1D9">  </span><span style="color: #FF7B72">const</span><span style="color: #C9D1D9"> </span><span style="color: #79C0FF"><data-lsp lsp="const unkey: Ratelimit">unkey</data-lsp></span><span style="color: #C9D1D9"> </span><span style="color: #FF7B72">=</span><span style="color: #C9D1D9"> </span><span style="color: #FF7B72">new</span><span style="color: #C9D1D9"> </span><span style="color: #D2A8FF"><data-lsp lsp="(alias) new Ratelimit(config: RatelimitConfig): Ratelimit
import Ratelimit">Ratelimit</data-lsp></span><span style="color: #C9D1D9">({</span></div><div class="line"><span style="color: #C9D1D9">    <data-lsp lsp="(property) rootKey: string">rootKey</data-lsp>: <data-lsp lsp="var process: NodeJS.Process">process</data-lsp>.<data-lsp lsp="(property) NodeJS.Process.env: NodeJS.ProcessEnv">env</data-lsp>.</span><span style="color: #79C0FF"><data-lsp lsp="string | undefined">UNKEY_ROOT_KEY</data-lsp></span><span style="color: #FF7B72">!</span><span style="color: #C9D1D9">,</span></div><div class="line"><span style="color: #C9D1D9">    <data-lsp lsp="(property) async?: boolean | undefined">async</data-lsp>: </span><span style="color: #79C0FF">true</span><span style="color: #C9D1D9">,</span></div><div class="line"><span style="color: #C9D1D9">    <data-lsp lsp="(property) duration: number | Duration">duration</data-lsp>: </span><span style="color: #A5D6FF">'10s'</span><span style="color: #C9D1D9">,</span></div><div class="line"><span style="color: #C9D1D9">    <data-lsp lsp="(property) limit: number">limit</data-lsp>: </span><span style="color: #79C0FF">5</span><span style="color: #C9D1D9">,</span></div><div class="line"><span style="color: #C9D1D9">    <data-lsp lsp="(property) namespace: string">namespace</data-lsp>: </span><span style="color: #A5D6FF">`trpc_${</span><span style="color: #C9D1D9"><data-lsp lsp="(parameter) opts: {
    ctx: {
        user: User;
    };
    type: ProcedureType;
    path: string;
    input: UnsetMarker;
    getRawInput: GetRawInputFn;
    meta: Meta | undefined;
    signal: AbortSignal | undefined;
    batchIndex: number;
    next: {
        (): Promise<MiddlewareResult<{
            user: User;
        }>>;
        <$ContextOverride>(opts: {
            ctx?: $ContextOverride | undefined;
            input?: unknown;
        }): Promise<MiddlewareResult<$ContextOverride>>;
        (opts: {
            getRawInput: GetRawInputFn;
        }): Promise<...>;
    };
}">opts</data-lsp></span><span style="color: #A5D6FF">.</span><span style="color: #C9D1D9"><data-lsp lsp="(property) path: string">path</data-lsp></span><span style="color: #A5D6FF">}`</span><span style="color: #C9D1D9">,</span></div><div class="line"><span style="color: #C9D1D9">  });</span></div><div class="line">&nbsp;</div><div class="line"><span style="color: #C9D1D9">  </span><span style="color: #FF7B72">const</span><span style="color: #C9D1D9"> </span><span style="color: #79C0FF"><data-lsp lsp="const ratelimit: RatelimitResponse">ratelimit</data-lsp></span><span style="color: #C9D1D9"> </span><span style="color: #FF7B72">=</span><span style="color: #C9D1D9"> </span><span style="color: #FF7B72">await</span><span style="color: #C9D1D9"> <data-lsp lsp="const unkey: Ratelimit">unkey</data-lsp>.</span><span style="color: #D2A8FF"><data-lsp lsp="(method) Ratelimit.limit(identifier: string, opts?: LimitOptions): Promise<RatelimitResponse>">limit</data-lsp></span><span style="color: #C9D1D9">(<data-lsp lsp="(parameter) opts: {
    ctx: {
        user: User;
    };
    type: ProcedureType;
    path: string;
    input: UnsetMarker;
    getRawInput: GetRawInputFn;
    meta: Meta | undefined;
    signal: AbortSignal | undefined;
    batchIndex: number;
    next: {
        (): Promise<MiddlewareResult<{
            user: User;
        }>>;
        <$ContextOverride>(opts: {
            ctx?: $ContextOverride | undefined;
            input?: unknown;
        }): Promise<MiddlewareResult<$ContextOverride>>;
        (opts: {
            getRawInput: GetRawInputFn;
        }): Promise<...>;
    };
}">opts</data-lsp>.<data-lsp lsp="(property) ctx: {
    user: User;
}">ctx</data-lsp>.<data-lsp lsp="(property) user: User">user</data-lsp>.<data-lsp lsp="(property) User.id: string">id</data-lsp>);</span></div><div class="line"><span style="color: #C9D1D9">  </span><span style="color: #FF7B72">if</span><span style="color: #C9D1D9"> (</span><span style="color: #FF7B72">!</span><span style="color: #C9D1D9"><data-lsp lsp="const ratelimit: RatelimitResponse">ratelimit</data-lsp>.<data-lsp lsp="(property) success: boolean">success</data-lsp>) {</span></div><div class="line"><span style="color: #C9D1D9">    </span><span style="color: #FF7B72">throw</span><span style="color: #C9D1D9"> </span><span style="color: #FF7B72">new</span><span style="color: #C9D1D9"> </span><span style="color: #D2A8FF"><data-lsp lsp="(alias) new TRPCError(opts: {
    message?: string;
    code: TRPC_ERROR_CODE_KEY;
    cause?: unknown;
}): TRPCError
import TRPCError">TRPCError</data-lsp></span><span style="color: #C9D1D9">({</span></div><div class="line"><span style="color: #C9D1D9">      <data-lsp lsp="(property) code: &quot;UNAUTHORIZED&quot; | &quot;PARSE_ERROR&quot; | &quot;BAD_REQUEST&quot; | &quot;INTERNAL_SERVER_ERROR&quot; | &quot;NOT_IMPLEMENTED&quot; | &quot;BAD_GATEWAY&quot; | &quot;SERVICE_UNAVAILABLE&quot; | &quot;GATEWAY_TIMEOUT&quot; | &quot;PAYMENT_REQUIRED&quot; | &quot;FORBIDDEN&quot; | &quot;NOT_FOUND&quot; | &quot;METHOD_NOT_SUPPORTED&quot; | &quot;TIMEOUT&quot; | &quot;CONFLICT&quot; | &quot;PRECONDITION_FAILED&quot; | &quot;PAYLOAD_TOO_LARGE&quot; | &quot;UNSUPPORTED_MEDIA_TYPE&quot; | &quot;UNPROCESSABLE_CONTENT&quot; | &quot;PRECONDITION_REQUIRED&quot; | &quot;TOO_MANY_REQUESTS&quot; | &quot;CLIENT_CLOSED_REQUEST&quot;">code</data-lsp>: </span><span style="color: #A5D6FF">'TOO_MANY_REQUESTS'</span><span style="color: #C9D1D9">,</span></div><div class="line"><span style="color: #C9D1D9">      <data-lsp lsp="(property) message?: string | undefined">message</data-lsp>: </span><span style="color: #79C0FF"><data-lsp lsp="var JSON: JSON">JSON</data-lsp></span><span style="color: #C9D1D9">.</span><span style="color: #79C0FF"><data-lsp lsp="(method) JSON.stringify(value: any, replacer?: (this: any, key: string, value: any) => any, space?: string | number): string (+1 overload)">stringify</data-lsp></span><span style="color: #C9D1D9">(<data-lsp lsp="const ratelimit: RatelimitResponse">ratelimit</data-lsp>),</span></div><div class="line"><span style="color: #C9D1D9">    });</span></div><div class="line"><span style="color: #C9D1D9">  }</span></div><div class="line">&nbsp;</div><div class="line"><span style="color: #C9D1D9">  </span><span style="color: #FF7B72">return</span><span style="color: #C9D1D9"> <data-lsp lsp="(parameter) opts: {
    ctx: {
        user: User;
    };
    type: ProcedureType;
    path: string;
    input: UnsetMarker;
    getRawInput: GetRawInputFn;
    meta: Meta | undefined;
    signal: AbortSignal | undefined;
    batchIndex: number;
    next: {
        (): Promise<MiddlewareResult<{
            user: User;
        }>>;
        <$ContextOverride>(opts: {
            ctx?: $ContextOverride | undefined;
            input?: unknown;
        }): Promise<MiddlewareResult<$ContextOverride>>;
        (opts: {
            getRawInput: GetRawInputFn;
        }): Promise<...>;
    };
}">opts</data-lsp>.</span><span style="color: #D2A8FF"><data-lsp lsp="(property) next: () => Promise<MiddlewareResult<{
    user: User;
}>> (+2 overloads)">next</data-lsp></span><span style="color: #C9D1D9">();</span></div><div class="line"><span style="color: #C9D1D9">});</span></div></code></div><button type="button" aria-label="Copy code to clipboard" class="copy-button" onclick="navigator.clipboard.writeText(this.previousSibling.innerText),this.classList.add(&quot;copied&quot;),this.textContent=&quot;Copied&quot;,setTimeout(()=>{this.classList.remove(&quot;copied&quot;),this.textContent=&quot;Copy&quot;},2e3)">Copy</button></pre></div>
<div><pre class="shiki min-light with-title twoslash lsp" style="background-color: #ffffff; color: #24292eff" title="app/_actions.ts"><div class="code-title">app/_actions.ts</div><div class="language-id">ts</div><div class="code-container"><code><div class="line"><span style="color: #22863A">'use server'</span><span style="color: #24292EFF">;</span></div><div class="line">&nbsp;</div><div class="line"><span style="color: #D32F2F">import</span><span style="color: #24292EFF"> { <data-lsp lsp="import z">z</data-lsp> } </span><span style="color: #D32F2F">from</span><span style="color: #24292EFF"> </span><span style="color: #22863A">'zod'</span><span style="color: #24292EFF">;</span></div><div class="line"><span style="color: #D32F2F">import</span><span style="color: #24292EFF"> { <data-lsp lsp="(alias) const rateLimitedAction: ProcedureBuilder<object, Meta, {
    user: User;
}, UnsetMarker, UnsetMarker, UnsetMarker, UnsetMarker, true>
import rateLimitedAction">rateLimitedAction</data-lsp> } </span><span style="color: #D32F2F">from</span><span style="color: #24292EFF"> </span><span style="color: #22863A">'../server/trpc'</span><span style="color: #24292EFF">;</span></div><div class="line">&nbsp;</div><div class="line"><span style="color: #D32F2F">export</span><span style="color: #24292EFF"> </span><span style="color: #D32F2F">const</span><span style="color: #24292EFF"> </span><span style="color: #1976D2"><data-lsp lsp="const commentOnPost: (input: {
    postId: string;
    content: string;
}) => Promise<void>">commentOnPost</data-lsp></span><span style="color: #24292EFF"> </span><span style="color: #D32F2F">=</span><span style="color: #24292EFF"> <data-lsp lsp="(alias) const rateLimitedAction: ProcedureBuilder<object, Meta, {
    user: User;
}, UnsetMarker, UnsetMarker, UnsetMarker, UnsetMarker, true>
import rateLimitedAction">rateLimitedAction</data-lsp></span></div><div class="line"><span style="color: #24292EFF">  </span><span style="color: #6F42C1">.<data-lsp lsp="(method) ProcedureBuilder<object, Meta, { user: User; }, UnsetMarker, UnsetMarker, UnsetMarker, UnsetMarker, true>.input<z.ZodObject<{
    postId: z.ZodString;
    content: z.ZodString;
}, z.core.$strip>>(schema: z.ZodObject<{
    postId: z.ZodString;
    content: z.ZodString;
}, z.core.$strip>): ProcedureBuilder<object, Meta, {
    user: User;
}, {
    postId: string;
    content: string;
}, {
    postId: string;
    content: string;
}, UnsetMarker, UnsetMarker, true>">input</data-lsp></span><span style="color: #24292EFF">(</span></div><div class="line"><span style="color: #24292EFF">    </span><span style="color: #1976D2"><data-lsp lsp="import z">z</data-lsp></span><span style="color: #6F42C1">.<data-lsp lsp="function object<{
    postId: z.ZodString;
    content: z.ZodString;
}>(shape?: {
    postId: z.ZodString;
    content: z.ZodString;
} | undefined, params?: string | {
    error?: string | z.core.$ZodErrorMap<NonNullable<z.core.$ZodIssueUnrecognizedKeys | z.core.$ZodIssueInvalidType<unknown>>> | undefined;
    message?: string | undefined | undefined;
} | undefined): z.ZodObject<{
    postId: z.ZodString;
    content: z.ZodString;
}, z.core.$strip>">object</data-lsp></span><span style="color: #24292EFF">({</span></div><div class="line"><span style="color: #24292EFF">      <data-lsp lsp="(property) postId: z.ZodString">postId</data-lsp></span><span style="color: #D32F2F">:</span><span style="color: #24292EFF"> </span><span style="color: #1976D2"><data-lsp lsp="import z">z</data-lsp></span><span style="color: #6F42C1">.<data-lsp lsp="function string(params?: string | z.core.$ZodStringParams): z.ZodString (+1 overload)">string</data-lsp></span><span style="color: #24292EFF">()</span><span style="color: #212121">,</span></div><div class="line"><span style="color: #24292EFF">      <data-lsp lsp="(property) content: z.ZodString">content</data-lsp></span><span style="color: #D32F2F">:</span><span style="color: #24292EFF"> </span><span style="color: #1976D2"><data-lsp lsp="import z">z</data-lsp></span><span style="color: #6F42C1">.<data-lsp lsp="function string(params?: string | z.core.$ZodStringParams): z.ZodString (+1 overload)">string</data-lsp></span><span style="color: #24292EFF">()</span><span style="color: #212121">,</span></div><div class="line"><span style="color: #24292EFF">    })</span><span style="color: #212121">,</span></div><div class="line"><span style="color: #24292EFF">  )</span></div><div class="line"><span style="color: #24292EFF">  </span><span style="color: #6F42C1">.<data-lsp lsp="(method) ProcedureBuilder<object, Meta, { user: User; }, { postId: string; content: string; }, { postId: string; content: string; }, UnsetMarker, UnsetMarker, true>.mutation<void>(resolver: ProcedureResolver<object, Meta, {
    user: User;
}, {
    postId: string;
    content: string;
}, UnsetMarker, void>): (input: {
    postId: string;
    content: string;
}) => Promise<void>">mutation</data-lsp></span><span style="color: #24292EFF">(</span><span style="color: #D32F2F">async</span><span style="color: #24292EFF"> (<data-lsp lsp="(parameter) opts: ProcedureResolverOptions<object, Meta, {
    user: User;
}, {
    postId: string;
    content: string;
}>">opts</data-lsp>) </span><span style="color: #D32F2F">=&gt;</span><span style="color: #24292EFF"> {</span></div><div class="line"><span style="color: #24292EFF">    </span><span style="color: #1976D2"><data-lsp lsp="namespace console
var console: Console">console</data-lsp></span><span style="color: #6F42C1">.<data-lsp lsp="(method) Console.log(message?: any, ...optionalParams: any[]): void (+1 overload)">log</data-lsp></span><span style="color: #24292EFF">(</span></div><div class="line"><span style="color: #24292EFF">      </span><span style="color: #22863A">`</span><span style="color: #D32F2F">${</span><span style="color: #1976D2"><data-lsp lsp="(parameter) opts: ProcedureResolverOptions<object, Meta, {
    user: User;
}, {
    postId: string;
    content: string;
}>">opts</data-lsp></span><span style="color: #24292EFF">.</span><span style="color: #1976D2"><data-lsp lsp="(property) ProcedureResolverOptions<object, Meta, { user: User; }, { postId: string; content: string; }>.ctx: {
    user: User;
}">ctx</data-lsp></span><span style="color: #24292EFF">.</span><span style="color: #1976D2"><data-lsp lsp="(property) user: User">user</data-lsp></span><span style="color: #24292EFF">.<data-lsp lsp="(property) User.name: string">name</data-lsp></span><span style="color: #D32F2F">}</span><span style="color: #22863A"> commented on </span><span style="color: #D32F2F">${</span><span style="color: #1976D2"><data-lsp lsp="(parameter) opts: ProcedureResolverOptions<object, Meta, {
    user: User;
}, {
    postId: string;
    content: string;
}>">opts</data-lsp></span><span style="color: #24292EFF">.</span><span style="color: #1976D2"><data-lsp lsp="(property) ProcedureResolverOptions<object, Meta, { user: User; }, { postId: string; content: string; }>.input: {
    postId: string;
    content: string;
}">input</data-lsp></span><span style="color: #24292EFF">.<data-lsp lsp="(property) postId: string">postId</data-lsp></span><span style="color: #D32F2F">}</span><span style="color: #22863A"> saying </span><span style="color: #D32F2F">${</span><span style="color: #1976D2"><data-lsp lsp="(parameter) opts: ProcedureResolverOptions<object, Meta, {
    user: User;
}, {
    postId: string;
    content: string;
}>">opts</data-lsp></span><span style="color: #24292EFF">.</span><span style="color: #1976D2"><data-lsp lsp="(property) ProcedureResolverOptions<object, Meta, { user: User; }, { postId: string; content: string; }>.input: {
    postId: string;
    content: string;
}">input</data-lsp></span><span style="color: #24292EFF">.<data-lsp lsp="(property) content: string">content</data-lsp></span><span style="color: #D32F2F">}</span><span style="color: #22863A">`</span><span style="color: #212121">,</span></div><div class="line"><span style="color: #24292EFF">    );</span></div><div class="line"><span style="color: #24292EFF">  });</span></div></code></div><button type="button" aria-label="Copy code to clipboard" class="copy-button" onclick="navigator.clipboard.writeText(this.previousSibling.innerText),this.classList.add(&quot;copied&quot;),this.textContent=&quot;Copied&quot;,setTimeout(()=>{this.classList.remove(&quot;copied&quot;),this.textContent=&quot;Copy&quot;},2e3)">Copy</button></pre>
<pre class="shiki github-dark with-title twoslash lsp" style="background-color: #0d1117; color: #c9d1d9" title="app/_actions.ts"><div class="code-title">app/_actions.ts</div><div class="language-id">ts</div><div class="code-container"><code><div class="line"><span style="color: #A5D6FF">'use server'</span><span style="color: #C9D1D9">;</span></div><div class="line">&nbsp;</div><div class="line"><span style="color: #FF7B72">import</span><span style="color: #C9D1D9"> { <data-lsp lsp="import z">z</data-lsp> } </span><span style="color: #FF7B72">from</span><span style="color: #C9D1D9"> </span><span style="color: #A5D6FF">'zod'</span><span style="color: #C9D1D9">;</span></div><div class="line"><span style="color: #FF7B72">import</span><span style="color: #C9D1D9"> { <data-lsp lsp="(alias) const rateLimitedAction: ProcedureBuilder<object, Meta, {
    user: User;
}, UnsetMarker, UnsetMarker, UnsetMarker, UnsetMarker, true>
import rateLimitedAction">rateLimitedAction</data-lsp> } </span><span style="color: #FF7B72">from</span><span style="color: #C9D1D9"> </span><span style="color: #A5D6FF">'../server/trpc'</span><span style="color: #C9D1D9">;</span></div><div class="line">&nbsp;</div><div class="line"><span style="color: #FF7B72">export</span><span style="color: #C9D1D9"> </span><span style="color: #FF7B72">const</span><span style="color: #C9D1D9"> </span><span style="color: #79C0FF"><data-lsp lsp="const commentOnPost: (input: {
    postId: string;
    content: string;
}) => Promise<void>">commentOnPost</data-lsp></span><span style="color: #C9D1D9"> </span><span style="color: #FF7B72">=</span><span style="color: #C9D1D9"> <data-lsp lsp="(alias) const rateLimitedAction: ProcedureBuilder<object, Meta, {
    user: User;
}, UnsetMarker, UnsetMarker, UnsetMarker, UnsetMarker, true>
import rateLimitedAction">rateLimitedAction</data-lsp></span></div><div class="line"><span style="color: #C9D1D9">  .</span><span style="color: #D2A8FF"><data-lsp lsp="(method) ProcedureBuilder<object, Meta, { user: User; }, UnsetMarker, UnsetMarker, UnsetMarker, UnsetMarker, true>.input<z.ZodObject<{
    postId: z.ZodString;
    content: z.ZodString;
}, z.core.$strip>>(schema: z.ZodObject<{
    postId: z.ZodString;
    content: z.ZodString;
}, z.core.$strip>): ProcedureBuilder<object, Meta, {
    user: User;
}, {
    postId: string;
    content: string;
}, {
    postId: string;
    content: string;
}, UnsetMarker, UnsetMarker, true>">input</data-lsp></span><span style="color: #C9D1D9">(</span></div><div class="line"><span style="color: #C9D1D9">    <data-lsp lsp="import z">z</data-lsp>.</span><span style="color: #D2A8FF"><data-lsp lsp="function object<{
    postId: z.ZodString;
    content: z.ZodString;
}>(shape?: {
    postId: z.ZodString;
    content: z.ZodString;
} | undefined, params?: string | {
    error?: string | z.core.$ZodErrorMap<NonNullable<z.core.$ZodIssueUnrecognizedKeys | z.core.$ZodIssueInvalidType<unknown>>> | undefined;
    message?: string | undefined | undefined;
} | undefined): z.ZodObject<{
    postId: z.ZodString;
    content: z.ZodString;
}, z.core.$strip>">object</data-lsp></span><span style="color: #C9D1D9">({</span></div><div class="line"><span style="color: #C9D1D9">      <data-lsp lsp="(property) postId: z.ZodString">postId</data-lsp>: <data-lsp lsp="import z">z</data-lsp>.</span><span style="color: #D2A8FF"><data-lsp lsp="function string(params?: string | z.core.$ZodStringParams): z.ZodString (+1 overload)">string</data-lsp></span><span style="color: #C9D1D9">(),</span></div><div class="line"><span style="color: #C9D1D9">      <data-lsp lsp="(property) content: z.ZodString">content</data-lsp>: <data-lsp lsp="import z">z</data-lsp>.</span><span style="color: #D2A8FF"><data-lsp lsp="function string(params?: string | z.core.$ZodStringParams): z.ZodString (+1 overload)">string</data-lsp></span><span style="color: #C9D1D9">(),</span></div><div class="line"><span style="color: #C9D1D9">    }),</span></div><div class="line"><span style="color: #C9D1D9">  )</span></div><div class="line"><span style="color: #C9D1D9">  .</span><span style="color: #D2A8FF"><data-lsp lsp="(method) ProcedureBuilder<object, Meta, { user: User; }, { postId: string; content: string; }, { postId: string; content: string; }, UnsetMarker, UnsetMarker, true>.mutation<void>(resolver: ProcedureResolver<object, Meta, {
    user: User;
}, {
    postId: string;
    content: string;
}, UnsetMarker, void>): (input: {
    postId: string;
    content: string;
}) => Promise<void>">mutation</data-lsp></span><span style="color: #C9D1D9">(</span><span style="color: #FF7B72">async</span><span style="color: #C9D1D9"> (</span><span style="color: #FFA657"><data-lsp lsp="(parameter) opts: ProcedureResolverOptions<object, Meta, {
    user: User;
}, {
    postId: string;
    content: string;
}>">opts</data-lsp></span><span style="color: #C9D1D9">) </span><span style="color: #FF7B72">=&gt;</span><span style="color: #C9D1D9"> {</span></div><div class="line"><span style="color: #C9D1D9">    <data-lsp lsp="namespace console
var console: Console">console</data-lsp>.</span><span style="color: #D2A8FF"><data-lsp lsp="(method) Console.log(message?: any, ...optionalParams: any[]): void (+1 overload)">log</data-lsp></span><span style="color: #C9D1D9">(</span></div><div class="line"><span style="color: #C9D1D9">      </span><span style="color: #A5D6FF">`${</span><span style="color: #C9D1D9"><data-lsp lsp="(parameter) opts: ProcedureResolverOptions<object, Meta, {
    user: User;
}, {
    postId: string;
    content: string;
}>">opts</data-lsp></span><span style="color: #A5D6FF">.</span><span style="color: #C9D1D9"><data-lsp lsp="(property) ProcedureResolverOptions<object, Meta, { user: User; }, { postId: string; content: string; }>.ctx: {
    user: User;
}">ctx</data-lsp></span><span style="color: #A5D6FF">.</span><span style="color: #C9D1D9"><data-lsp lsp="(property) user: User">user</data-lsp></span><span style="color: #A5D6FF">.</span><span style="color: #C9D1D9"><data-lsp lsp="(property) User.name: string">name</data-lsp></span><span style="color: #A5D6FF">} commented on ${</span><span style="color: #C9D1D9"><data-lsp lsp="(parameter) opts: ProcedureResolverOptions<object, Meta, {
    user: User;
}, {
    postId: string;
    content: string;
}>">opts</data-lsp></span><span style="color: #A5D6FF">.</span><span style="color: #C9D1D9"><data-lsp lsp="(property) ProcedureResolverOptions<object, Meta, { user: User; }, { postId: string; content: string; }>.input: {
    postId: string;
    content: string;
}">input</data-lsp></span><span style="color: #A5D6FF">.</span><span style="color: #C9D1D9"><data-lsp lsp="(property) postId: string">postId</data-lsp></span><span style="color: #A5D6FF">} saying ${</span><span style="color: #C9D1D9"><data-lsp lsp="(parameter) opts: ProcedureResolverOptions<object, Meta, {
    user: User;
}, {
    postId: string;
    content: string;
}>">opts</data-lsp></span><span style="color: #A5D6FF">.</span><span style="color: #C9D1D9"><data-lsp lsp="(property) ProcedureResolverOptions<object, Meta, { user: User; }, { postId: string; content: string; }>.input: {
    postId: string;
    content: string;
}">input</data-lsp></span><span style="color: #A5D6FF">.</span><span style="color: #C9D1D9"><data-lsp lsp="(property) content: string">content</data-lsp></span><span style="color: #A5D6FF">}`</span><span style="color: #C9D1D9">,</span></div><div class="line"><span style="color: #C9D1D9">    );</span></div><div class="line"><span style="color: #C9D1D9">  });</span></div></code></div><button type="button" aria-label="Copy code to clipboard" class="copy-button" onclick="navigator.clipboard.writeText(this.previousSibling.innerText),this.classList.add(&quot;copied&quot;),this.textContent=&quot;Copied&quot;,setTimeout(()=>{this.classList.remove(&quot;copied&quot;),this.textContent=&quot;Copy&quot;},2e3)">Copy</button></pre></div>
<p>Läs mer om att begränsa anrop till dina tRPC-procedurer <a href="https://www.unkey.com/blog/ratelimit-trpc-routes" target="_blank" rel="noopener noreferrer">i det här inlägget från Unkey</a>.</p>
<p>Möjligheterna är oändliga, och jag slår vad om att du redan har en massa bra verktygsmiddlewares som du använder i dina tRPC-applikationer idag. Om inte, kanske du hittar några du kan <code>npm install</code>a!</p>
<h2 class="anchor anchorWithStickyNavbar_MYIC" id="avslutningsvis">Avslutningsvis<a href="https://trpc.io/sv/blog/trpc-actions#avslutningsvis" class="hash-link" aria-label="Direktlänk till Avslutningsvis" title="Direktlänk till Avslutningsvis">​</a></h2>
<p>Serveråtgärder är inget silverkul. I situationer som kräver mer dynamisk data kanske du vill behålla din data i React Queries klient-sidcache och göra mutationer med <code>useMutation</code> istället. Det är helt legitimt. Dessa nya primitiv bör också vara lätta att adoptera gradvis, så du kan flytta individuella procedurer från din befintliga tRPC-API till serveråtgärder där det är meningsfullt. Det finns inget behov av att skriva om hela ditt API.</p>
<p>Genom att definiera dina serveråtgärder med tRPC kan du återanvända mycket av samma logik du använder idag och välja var du exponerar mutationen som en serveråtgärd eller som en mer traditionell mutation. Du som utvecklare har makten att välja vilka mönster som fungerar bäst för din applikation. Om du inte använder tRPC idag finns det några paket (<a href="https://github.com/TheEdoRan/next-safe-action" target="_blank" rel="noopener noreferrer">next-safe-action</a> och <a href="https://github.com/IdoPesok/zsa" target="_blank" rel="noopener noreferrer">zsa</a> kommer att tänka på) som låter dig definiera typsäkra, indatavaliderade serveråtgärder som också är värda att kolla in.</p>
<p>Om du vill se en app som använder detta i praktiken, kolla in <a href="https://trellix-trpc.vercel.app/" target="_blank" rel="noopener noreferrer">Trellix tRPC</a>, en app jag nyligen gjorde som utnyttjar dessa nya primitiv.</p>
<h3 class="anchor anchorWithStickyNavbar_MYIC" id="vad-tycker-du-vi-vill-ha-din-feedback">Vad tycker du? Vi vill ha din feedback<a href="https://trpc.io/sv/blog/trpc-actions#vad-tycker-du-vi-vill-ha-din-feedback" class="hash-link" aria-label="Direktlänk till Vad tycker du? Vi vill ha din feedback" title="Direktlänk till Vad tycker du? Vi vill ha din feedback">​</a></h3>
<p>Så, vad tycker du? Berätta för oss på <a href="https://github.com/trpc/trpc/discussions/5737" target="_blank" rel="noopener noreferrer">Github</a> och hjälp oss att iterera för att få dessa primitiv till ett stabilt tillstånd.</p>
<p>Det finns fortfarande arbete kvar, särskilt när det gäller felhantering. Next.js förespråkar att returnera fel, och vi vill göra detta så typsäkert som möjligt. Kolla in <a href="https://github.com/trpc/trpc/pull/5554" target="_blank" rel="noopener noreferrer">denna pågående PR av Alex</a> för tidigt arbete på detta.</p>
<p>Tills nästa gång, lycka till med kodandet!</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Bygga en minimal tRPC-klient]]></title>
            <link>https://trpc.io/sv/blog/tinyrpc-client</link>
            <guid>https://trpc.io/sv/blog/tinyrpc-client</guid>
            <pubDate>Tue, 17 Jan 2023 00:00:00 GMT</pubDate>
            <description><![CDATA[Denna sida har översatts av PageTurner AI (beta). Inte officiellt godkänd av projektet.]]></description>
            <content:encoded><![CDATA[<div class="theme-admonition theme-admonition-info admonition_v5Ag alert alert--info"><div class="admonitionHeading_usrK"><span class="admonitionIcon_bgEp"><svg viewBox="0 0 14 16"><path fill-rule="evenodd" d="M7 2.3c3.14 0 5.7 2.56 5.7 5.7s-2.56 5.7-5.7 5.7A5.71 5.71 0 0 1 1.3 8c0-3.14 2.56-5.7 5.7-5.7zM7 1C3.14 1 0 4.14 0 8s3.14 7 7 7 7-3.14 7-7-3.14-7-7-7zm1 3H6v5h2V4zm0 6H6v2h2v-2z"></path></svg></span>Inofficiell Beta-översättning</div><div class="admonitionContent_e2NW"><p>Denna sida har översatts av <a href="https://page-turner.com/" target="_blank" rel="noopener noreferrer"><strong>PageTurner</strong></a> AI (beta). Inte officiellt godkänd av projektet.
Hittade du ett fel? <a href="https://feedback.page-turner.com/?repo_id=683d130a-1828-4b22-91cd-ef2d269ef3f5&amp;file_path=blog%2F2023-01-17-tinyrpc-client.mdx&amp;locale=sv" target="_blank" rel="noopener noreferrer">Rapportera problem →</a></p></div></div>
<p>Har du någonsin undrat hur tRPC fungerar? Kanske du vill börja bidra till projektet men är skrämd av internals? Syftet med detta inlägg är att bekanta dig med tRPC<!-- -->:s<!-- --> interna delar genom att bygga en minimal klient som täcker de stora delarna av hur tRPC fungerar.</p>
<!-- -->
<div class="theme-admonition theme-admonition-info admonition_v5Ag alert alert--info"><div class="admonitionHeading_usrK"><span class="admonitionIcon_bgEp"><svg viewBox="0 0 14 16"><path fill-rule="evenodd" d="M7 2.3c3.14 0 5.7 2.56 5.7 5.7s-2.56 5.7-5.7 5.7A5.71 5.71 0 0 1 1.3 8c0-3.14 2.56-5.7 5.7-5.7zM7 1C3.14 1 0 4.14 0 8s3.14 7 7 7 7-3.14 7-7-3.14-7-7-7zm1 3H6v5h2V4zm0 6H6v2h2v-2z"></path></svg></span>information</div><div class="admonitionContent_e2NW"><p>Det rekommenderas att du förstår några grundläggande koncept i TypeScript som generics, villkorsstyper, nyckelordet <code>extends</code> och rekursion. Om du inte är bekant med dessa rekommenderar jag att du går igenom <a href="https://twitter.com/mattpocockuk" target="_blank" rel="noopener noreferrer">Matt Pocock</a>s <a href="https://github.com/total-typescript/beginners-typescript-tutorial" target="_blank" rel="noopener noreferrer">Beginner TypeScript</a>-tutorial för att sätta dig in i dessa koncept innan du fortsätter läsa.</p></div></div>
<h2 class="anchor anchorWithStickyNavbar_MYIC" id="översikt">Översikt<a href="https://trpc.io/sv/blog/tinyrpc-client#%C3%B6versikt" class="hash-link" aria-label="Direktlänk till Översikt" title="Direktlänk till Översikt">​</a></h2>
<p>Låt oss anta att vi har en enkel tRPC-router med tre procedurer som ser ut så här:</p>
<div></div>
<div><pre class="shiki min-light twoslash lsp" style="background-color: #ffffff; color: #24292eff"><div class="language-id">ts</div><div class="code-container"><code><div class="line"><span style="color: #D32F2F">type</span><span style="color: #24292EFF"> </span><span style="color: #6F42C1"><data-lsp lsp="type Post = {
    id: string;
    title: string;
}">Post</data-lsp></span><span style="color: #24292EFF"> </span><span style="color: #D32F2F">=</span><span style="color: #24292EFF"> { <data-lsp lsp="(property) id: string">id</data-lsp></span><span style="color: #D32F2F">:</span><span style="color: #24292EFF"> </span><span style="color: #1976D2">string</span><span style="color: #24292EFF">; <data-lsp lsp="(property) title: string">title</data-lsp></span><span style="color: #D32F2F">:</span><span style="color: #24292EFF"> </span><span style="color: #1976D2">string</span><span style="color: #24292EFF"> };</span></div><div class="line"><span style="color: #D32F2F">const</span><span style="color: #24292EFF"> </span><span style="color: #1976D2"><data-lsp lsp="const posts: Post[]">posts</data-lsp></span><span style="color: #D32F2F">:</span><span style="color: #24292EFF"> </span><span style="color: #6F42C1"><data-lsp lsp="type Post = {
    id: string;
    title: string;
}">Post</data-lsp></span><span style="color: #24292EFF">[] </span><span style="color: #D32F2F">=</span><span style="color: #24292EFF"> [];</span></div><div class="line">&nbsp;</div><div class="line"><span style="color: #D32F2F">const</span><span style="color: #24292EFF"> </span><span style="color: #1976D2"><data-lsp lsp="const appRouter: BuiltRouter<{
    ctx: object;
    meta: object;
    errorShape: DefaultErrorShape;
    transformer: false;
}, DecorateCreateRouterOptions<{
    post: BuiltRouter<{
        ctx: object;
        meta: object;
        errorShape: DefaultErrorShape;
        transformer: false;
    }, DecorateCreateRouterOptions<{
        byId: QueryProcedure<{
            input: {
                id: string;
            };
            output: Post;
            meta: object;
        }>;
        byTitle: QueryProcedure<{
            input: {
                title: string;
            };
            output: Post;
            meta: object;
        }>;
        create: MutationProcedure<{
            input: {
                title: string;
            };
            output: {
                title: string;
                id: string;
            };
            meta: object;
        }>;
    }>>;
}>>">appRouter</data-lsp></span><span style="color: #24292EFF"> </span><span style="color: #D32F2F">=</span><span style="color: #24292EFF"> </span><span style="color: #6F42C1"><data-lsp lsp="const router: RouterBuilder
<{
    post: BuiltRouter<{
        ctx: object;
        meta: object;
        errorShape: DefaultErrorShape;
        transformer: false;
    }, DecorateCreateRouterOptions<{
        byId: QueryProcedure<{
            input: {
                id: string;
            };
            output: Post;
            meta: object;
        }>;
        byTitle: QueryProcedure<{
            input: {
                title: string;
            };
            output: Post;
            meta: object;
        }>;
        create: MutationProcedure<{
            input: {
                title: string;
            };
            output: {
                title: string;
                id: string;
            };
            meta: object;
        }>;
    }>>;
}>(_: {
    post: BuiltRouter<{
        ctx: object;
        meta: object;
        errorShape: DefaultErrorShape;
        transformer: false;
    }, DecorateCreateRouterOptions<{
        byId: QueryProcedure<{
            input: {
                id: string;
            };
            output: Post;
            meta: object;
        }>;
        byTitle: QueryProcedure<{
            input: {
                title: string;
            };
            output: Post;
            meta: object;
        }>;
        create: MutationProcedure<{
            input: {
                title: string;
            };
            output: {
                title: string;
                id: string;
            };
            meta: object;
        }>;
    }>>;
}) => BuiltRouter<...>">router</data-lsp></span><span style="color: #24292EFF">({</span></div><div class="line"><span style="color: #24292EFF">  <data-lsp lsp="(property) post: BuiltRouter<{
    ctx: object;
    meta: object;
    errorShape: DefaultErrorShape;
    transformer: false;
}, DecorateCreateRouterOptions<{
    byId: QueryProcedure<{
        input: {
            id: string;
        };
        output: Post;
        meta: object;
    }>;
    byTitle: QueryProcedure<{
        input: {
            title: string;
        };
        output: Post;
        meta: object;
    }>;
    create: MutationProcedure<{
        input: {
            title: string;
        };
        output: {
            title: string;
            id: string;
        };
        meta: object;
    }>;
}>>">post</data-lsp></span><span style="color: #D32F2F">:</span><span style="color: #24292EFF"> </span><span style="color: #6F42C1"><data-lsp lsp="const router: RouterBuilder
<{
    byId: QueryProcedure<{
        input: {
            id: string;
        };
        output: Post;
        meta: object;
    }>;
    byTitle: QueryProcedure<{
        input: {
            title: string;
        };
        output: Post;
        meta: object;
    }>;
    create: MutationProcedure<{
        input: {
            title: string;
        };
        output: {
            title: string;
            id: string;
        };
        meta: object;
    }>;
}>(_: {
    byId: QueryProcedure<{
        input: {
            id: string;
        };
        output: Post;
        meta: object;
    }>;
    byTitle: QueryProcedure<{
        input: {
            title: string;
        };
        output: Post;
        meta: object;
    }>;
    create: MutationProcedure<{
        input: {
            title: string;
        };
        output: {
            title: string;
            id: string;
        };
        meta: object;
    }>;
}) => BuiltRouter<...>">router</data-lsp></span><span style="color: #24292EFF">({</span></div><div class="line"><span style="color: #24292EFF">    <data-lsp lsp="(property) byId: QueryProcedure<{
    input: {
        id: string;
    };
    output: Post;
    meta: object;
}>">byId</data-lsp></span><span style="color: #D32F2F">:</span><span style="color: #24292EFF"> <data-lsp lsp="const publicProcedure: ProcedureBuilder<object, object, object, UnsetMarker, UnsetMarker, UnsetMarker, UnsetMarker, false>">publicProcedure</data-lsp></span></div><div class="line"><span style="color: #24292EFF">      </span><span style="color: #6F42C1">.<data-lsp lsp="(method) ProcedureBuilder<object, object, object, UnsetMarker, UnsetMarker, UnsetMarker, UnsetMarker, false>.input<z.ZodObject<{
    id: z.ZodString;
}, z.core.$strip>>(schema: z.ZodObject<{
    id: z.ZodString;
}, z.core.$strip>): ProcedureBuilder<object, object, object, {
    id: string;
}, {
    id: string;
}, UnsetMarker, UnsetMarker, false>">input</data-lsp></span><span style="color: #24292EFF">(</span><span style="color: #1976D2"><data-lsp lsp="import z">z</data-lsp></span><span style="color: #6F42C1">.<data-lsp lsp="function object<{
    id: z.ZodString;
}>(shape?: {
    id: z.ZodString;
} | undefined, params?: string | {
    error?: string | z.core.$ZodErrorMap<NonNullable<z.core.$ZodIssueUnrecognizedKeys | z.core.$ZodIssueInvalidType<unknown>>> | undefined;
    message?: string | undefined | undefined;
} | undefined): z.ZodObject<{
    id: z.ZodString;
}, z.core.$strip>">object</data-lsp></span><span style="color: #24292EFF">({ <data-lsp lsp="(property) id: z.ZodString">id</data-lsp></span><span style="color: #D32F2F">:</span><span style="color: #24292EFF"> </span><span style="color: #1976D2"><data-lsp lsp="import z">z</data-lsp></span><span style="color: #6F42C1">.<data-lsp lsp="function string(params?: string | z.core.$ZodStringParams): z.ZodString (+1 overload)">string</data-lsp></span><span style="color: #24292EFF">() }))</span></div><div class="line"><span style="color: #24292EFF">      </span><span style="color: #6F42C1">.<data-lsp lsp="(method) ProcedureBuilder<object, object, object, { id: string; }, { id: string; }, UnsetMarker, UnsetMarker, false>.query<Post>(resolver: ProcedureResolver<object, object, object, {
    id: string;
}, UnsetMarker, Post>): QueryProcedure<{
    input: {
        id: string;
    };
    output: Post;
    meta: object;
}>">query</data-lsp></span><span style="color: #24292EFF">(({ <data-lsp lsp="(parameter) input: {
    id: string;
}">input</data-lsp> }) </span><span style="color: #D32F2F">=&gt;</span><span style="color: #24292EFF"> {</span></div><div class="line"><span style="color: #24292EFF">        </span><span style="color: #D32F2F">const</span><span style="color: #24292EFF"> </span><span style="color: #1976D2"><data-lsp lsp="const post: Post | undefined">post</data-lsp></span><span style="color: #24292EFF"> </span><span style="color: #D32F2F">=</span><span style="color: #24292EFF"> </span><span style="color: #1976D2"><data-lsp lsp="const posts: Post[]">posts</data-lsp></span><span style="color: #6F42C1">.<data-lsp lsp="(method) Array<Post>.find(predicate: (value: Post, index: number, obj: Post[]) => unknown, thisArg?: any): Post | undefined (+1 overload)">find</data-lsp></span><span style="color: #24292EFF">((<data-lsp lsp="(parameter) p: Post">p</data-lsp>) </span><span style="color: #D32F2F">=&gt;</span><span style="color: #24292EFF"> </span><span style="color: #1976D2"><data-lsp lsp="(parameter) p: Post">p</data-lsp></span><span style="color: #24292EFF">.<data-lsp lsp="(property) id: string">id</data-lsp> </span><span style="color: #D32F2F">===</span><span style="color: #24292EFF"> </span><span style="color: #1976D2"><data-lsp lsp="(parameter) input: {
    id: string;
}">input</data-lsp></span><span style="color: #24292EFF">.<data-lsp lsp="(property) id: string">id</data-lsp>);</span></div><div class="line"><span style="color: #24292EFF">        </span><span style="color: #D32F2F">if</span><span style="color: #24292EFF"> (</span><span style="color: #D32F2F">!</span><span style="color: #24292EFF"><data-lsp lsp="const post: Post | undefined">post</data-lsp>) </span><span style="color: #D32F2F">throw</span><span style="color: #24292EFF"> </span><span style="color: #D32F2F">new</span><span style="color: #24292EFF"> </span><span style="color: #6F42C1"><data-lsp lsp="(alias) new TRPCError(opts: {
    message?: string;
    code: TRPC_ERROR_CODE_KEY;
    cause?: unknown;
}): TRPCError
import TRPCError">TRPCError</data-lsp></span><span style="color: #24292EFF">({ <data-lsp lsp="(property) code: &quot;NOT_FOUND&quot; | &quot;PARSE_ERROR&quot; | &quot;BAD_REQUEST&quot; | &quot;INTERNAL_SERVER_ERROR&quot; | &quot;NOT_IMPLEMENTED&quot; | &quot;BAD_GATEWAY&quot; | &quot;SERVICE_UNAVAILABLE&quot; | &quot;GATEWAY_TIMEOUT&quot; | &quot;UNAUTHORIZED&quot; | &quot;PAYMENT_REQUIRED&quot; | &quot;FORBIDDEN&quot; | &quot;METHOD_NOT_SUPPORTED&quot; | &quot;TIMEOUT&quot; | &quot;CONFLICT&quot; | &quot;PRECONDITION_FAILED&quot; | &quot;PAYLOAD_TOO_LARGE&quot; | &quot;UNSUPPORTED_MEDIA_TYPE&quot; | &quot;UNPROCESSABLE_CONTENT&quot; | &quot;PRECONDITION_REQUIRED&quot; | &quot;TOO_MANY_REQUESTS&quot; | &quot;CLIENT_CLOSED_REQUEST&quot;">code</data-lsp></span><span style="color: #D32F2F">:</span><span style="color: #24292EFF"> </span><span style="color: #22863A">"NOT_FOUND"</span><span style="color: #24292EFF"> });</span></div><div class="line"><span style="color: #24292EFF">        </span><span style="color: #D32F2F">return</span><span style="color: #24292EFF"> <data-lsp lsp="const post: Post">post</data-lsp>;</span></div><div class="line"><span style="color: #24292EFF">      })</span><span style="color: #212121">,</span></div><div class="line"><span style="color: #24292EFF">    <data-lsp lsp="(property) byTitle: QueryProcedure<{
    input: {
        title: string;
    };
    output: Post;
    meta: object;
}>">byTitle</data-lsp></span><span style="color: #D32F2F">:</span><span style="color: #24292EFF"> <data-lsp lsp="const publicProcedure: ProcedureBuilder<object, object, object, UnsetMarker, UnsetMarker, UnsetMarker, UnsetMarker, false>">publicProcedure</data-lsp></span></div><div class="line"><span style="color: #24292EFF">      </span><span style="color: #6F42C1">.<data-lsp lsp="(method) ProcedureBuilder<object, object, object, UnsetMarker, UnsetMarker, UnsetMarker, UnsetMarker, false>.input<z.ZodObject<{
    title: z.ZodString;
}, z.core.$strip>>(schema: z.ZodObject<{
    title: z.ZodString;
}, z.core.$strip>): ProcedureBuilder<object, object, object, {
    title: string;
}, {
    title: string;
}, UnsetMarker, UnsetMarker, false>">input</data-lsp></span><span style="color: #24292EFF">(</span><span style="color: #1976D2"><data-lsp lsp="import z">z</data-lsp></span><span style="color: #6F42C1">.<data-lsp lsp="function object<{
    title: z.ZodString;
}>(shape?: {
    title: z.ZodString;
} | undefined, params?: string | {
    error?: string | z.core.$ZodErrorMap<NonNullable<z.core.$ZodIssueUnrecognizedKeys | z.core.$ZodIssueInvalidType<unknown>>> | undefined;
    message?: string | undefined | undefined;
} | undefined): z.ZodObject<{
    title: z.ZodString;
}, z.core.$strip>">object</data-lsp></span><span style="color: #24292EFF">({ <data-lsp lsp="(property) title: z.ZodString">title</data-lsp></span><span style="color: #D32F2F">:</span><span style="color: #24292EFF"> </span><span style="color: #1976D2"><data-lsp lsp="import z">z</data-lsp></span><span style="color: #6F42C1">.<data-lsp lsp="function string(params?: string | z.core.$ZodStringParams): z.ZodString (+1 overload)">string</data-lsp></span><span style="color: #24292EFF">() }))</span></div><div class="line"><span style="color: #24292EFF">      </span><span style="color: #6F42C1">.<data-lsp lsp="(method) ProcedureBuilder<object, object, object, { title: string; }, { title: string; }, UnsetMarker, UnsetMarker, false>.query<Post>(resolver: ProcedureResolver<object, object, object, {
    title: string;
}, UnsetMarker, Post>): QueryProcedure<{
    input: {
        title: string;
    };
    output: Post;
    meta: object;
}>">query</data-lsp></span><span style="color: #24292EFF">(({ <data-lsp lsp="(parameter) input: {
    title: string;
}">input</data-lsp> }) </span><span style="color: #D32F2F">=&gt;</span><span style="color: #24292EFF"> {</span></div><div class="line"><span style="color: #24292EFF">        </span><span style="color: #D32F2F">const</span><span style="color: #24292EFF"> </span><span style="color: #1976D2"><data-lsp lsp="const post: Post | undefined">post</data-lsp></span><span style="color: #24292EFF"> </span><span style="color: #D32F2F">=</span><span style="color: #24292EFF"> </span><span style="color: #1976D2"><data-lsp lsp="const posts: Post[]">posts</data-lsp></span><span style="color: #6F42C1">.<data-lsp lsp="(method) Array<Post>.find(predicate: (value: Post, index: number, obj: Post[]) => unknown, thisArg?: any): Post | undefined (+1 overload)">find</data-lsp></span><span style="color: #24292EFF">((<data-lsp lsp="(parameter) p: Post">p</data-lsp>) </span><span style="color: #D32F2F">=&gt;</span><span style="color: #24292EFF"> </span><span style="color: #1976D2"><data-lsp lsp="(parameter) p: Post">p</data-lsp></span><span style="color: #24292EFF">.<data-lsp lsp="(property) title: string">title</data-lsp> </span><span style="color: #D32F2F">===</span><span style="color: #24292EFF"> </span><span style="color: #1976D2"><data-lsp lsp="(parameter) input: {
    title: string;
}">input</data-lsp></span><span style="color: #24292EFF">.<data-lsp lsp="(property) title: string">title</data-lsp>);</span></div><div class="line"><span style="color: #24292EFF">        </span><span style="color: #D32F2F">if</span><span style="color: #24292EFF"> (</span><span style="color: #D32F2F">!</span><span style="color: #24292EFF"><data-lsp lsp="const post: Post | undefined">post</data-lsp>) </span><span style="color: #D32F2F">throw</span><span style="color: #24292EFF"> </span><span style="color: #D32F2F">new</span><span style="color: #24292EFF"> </span><span style="color: #6F42C1"><data-lsp lsp="(alias) new TRPCError(opts: {
    message?: string;
    code: TRPC_ERROR_CODE_KEY;
    cause?: unknown;
}): TRPCError
import TRPCError">TRPCError</data-lsp></span><span style="color: #24292EFF">({ <data-lsp lsp="(property) code: &quot;NOT_FOUND&quot; | &quot;PARSE_ERROR&quot; | &quot;BAD_REQUEST&quot; | &quot;INTERNAL_SERVER_ERROR&quot; | &quot;NOT_IMPLEMENTED&quot; | &quot;BAD_GATEWAY&quot; | &quot;SERVICE_UNAVAILABLE&quot; | &quot;GATEWAY_TIMEOUT&quot; | &quot;UNAUTHORIZED&quot; | &quot;PAYMENT_REQUIRED&quot; | &quot;FORBIDDEN&quot; | &quot;METHOD_NOT_SUPPORTED&quot; | &quot;TIMEOUT&quot; | &quot;CONFLICT&quot; | &quot;PRECONDITION_FAILED&quot; | &quot;PAYLOAD_TOO_LARGE&quot; | &quot;UNSUPPORTED_MEDIA_TYPE&quot; | &quot;UNPROCESSABLE_CONTENT&quot; | &quot;PRECONDITION_REQUIRED&quot; | &quot;TOO_MANY_REQUESTS&quot; | &quot;CLIENT_CLOSED_REQUEST&quot;">code</data-lsp></span><span style="color: #D32F2F">:</span><span style="color: #24292EFF"> </span><span style="color: #22863A">"NOT_FOUND"</span><span style="color: #24292EFF"> });</span></div><div class="line"><span style="color: #24292EFF">        </span><span style="color: #D32F2F">return</span><span style="color: #24292EFF"> <data-lsp lsp="const post: Post">post</data-lsp>;</span></div><div class="line"><span style="color: #24292EFF">      })</span><span style="color: #212121">,</span></div><div class="line"><span style="color: #24292EFF">    <data-lsp lsp="(property) create: MutationProcedure<{
    input: {
        title: string;
    };
    output: {
        title: string;
        id: string;
    };
    meta: object;
}>">create</data-lsp></span><span style="color: #D32F2F">:</span><span style="color: #24292EFF"> <data-lsp lsp="const publicProcedure: ProcedureBuilder<object, object, object, UnsetMarker, UnsetMarker, UnsetMarker, UnsetMarker, false>">publicProcedure</data-lsp></span></div><div class="line"><span style="color: #24292EFF">      </span><span style="color: #6F42C1">.<data-lsp lsp="(method) ProcedureBuilder<object, object, object, UnsetMarker, UnsetMarker, UnsetMarker, UnsetMarker, false>.input<z.ZodObject<{
    title: z.ZodString;
}, z.core.$strip>>(schema: z.ZodObject<{
    title: z.ZodString;
}, z.core.$strip>): ProcedureBuilder<object, object, object, {
    title: string;
}, {
    title: string;
}, UnsetMarker, UnsetMarker, false>">input</data-lsp></span><span style="color: #24292EFF">(</span><span style="color: #1976D2"><data-lsp lsp="import z">z</data-lsp></span><span style="color: #6F42C1">.<data-lsp lsp="function object<{
    title: z.ZodString;
}>(shape?: {
    title: z.ZodString;
} | undefined, params?: string | {
    error?: string | z.core.$ZodErrorMap<NonNullable<z.core.$ZodIssueUnrecognizedKeys | z.core.$ZodIssueInvalidType<unknown>>> | undefined;
    message?: string | undefined | undefined;
} | undefined): z.ZodObject<{
    title: z.ZodString;
}, z.core.$strip>">object</data-lsp></span><span style="color: #24292EFF">({ <data-lsp lsp="(property) title: z.ZodString">title</data-lsp></span><span style="color: #D32F2F">:</span><span style="color: #24292EFF"> </span><span style="color: #1976D2"><data-lsp lsp="import z">z</data-lsp></span><span style="color: #6F42C1">.<data-lsp lsp="function string(params?: string | z.core.$ZodStringParams): z.ZodString (+1 overload)">string</data-lsp></span><span style="color: #24292EFF">() }))</span></div><div class="line"><span style="color: #24292EFF">      </span><span style="color: #6F42C1">.<data-lsp lsp="(method) ProcedureBuilder<object, object, object, { title: string; }, { title: string; }, UnsetMarker, UnsetMarker, false>.mutation<{
    title: string;
    id: string;
}>(resolver: ProcedureResolver<object, object, object, {
    title: string;
}, UnsetMarker, {
    title: string;
    id: string;
}>): MutationProcedure<{
    input: {
        title: string;
    };
    output: {
        title: string;
        id: string;
    };
    meta: object;
}>">mutation</data-lsp></span><span style="color: #24292EFF">(({ <data-lsp lsp="(parameter) input: {
    title: string;
}">input</data-lsp> }) </span><span style="color: #D32F2F">=&gt;</span><span style="color: #24292EFF"> {</span></div><div class="line"><span style="color: #24292EFF">        </span><span style="color: #D32F2F">const</span><span style="color: #24292EFF"> </span><span style="color: #1976D2"><data-lsp lsp="const post: {
    title: string;
    id: string;
}">post</data-lsp></span><span style="color: #24292EFF"> </span><span style="color: #D32F2F">=</span><span style="color: #24292EFF"> { <data-lsp lsp="(property) id: string">id</data-lsp></span><span style="color: #D32F2F">:</span><span style="color: #24292EFF"> </span><span style="color: #6F42C1"><data-lsp lsp="const uuid: () => string">uuid</data-lsp></span><span style="color: #24292EFF">()</span><span style="color: #212121">,</span><span style="color: #24292EFF"> </span><span style="color: #D32F2F">...</span><span style="color: #24292EFF"><data-lsp lsp="(parameter) input: {
    title: string;
}">input</data-lsp> };</span></div><div class="line"><span style="color: #24292EFF">        </span><span style="color: #1976D2"><data-lsp lsp="const posts: Post[]">posts</data-lsp></span><span style="color: #6F42C1">.<data-lsp lsp="(method) Array<Post>.push(...items: Post[]): number">push</data-lsp></span><span style="color: #24292EFF">(<data-lsp lsp="const post: {
    title: string;
    id: string;
}">post</data-lsp>);</span></div><div class="line"><span style="color: #24292EFF">        </span><span style="color: #D32F2F">return</span><span style="color: #24292EFF"> <data-lsp lsp="const post: {
    title: string;
    id: string;
}">post</data-lsp>;</span></div><div class="line"><span style="color: #24292EFF">      })</span><span style="color: #212121">,</span></div><div class="line"><span style="color: #24292EFF">  })</span><span style="color: #212121">,</span></div><div class="line"><span style="color: #24292EFF">});</span></div></code></div><button type="button" aria-label="Copy code to clipboard" class="copy-button" onclick="navigator.clipboard.writeText(this.previousSibling.innerText),this.classList.add(&quot;copied&quot;),this.textContent=&quot;Copied&quot;,setTimeout(()=>{this.classList.remove(&quot;copied&quot;),this.textContent=&quot;Copy&quot;},2e3)">Copy</button></pre>
<pre class="shiki github-dark twoslash lsp" style="background-color: #0d1117; color: #c9d1d9"><div class="language-id">ts</div><div class="code-container"><code><div class="line"><span style="color: #FF7B72">type</span><span style="color: #C9D1D9"> </span><span style="color: #FFA657"><data-lsp lsp="type Post = {
    id: string;
    title: string;
}">Post</data-lsp></span><span style="color: #C9D1D9"> </span><span style="color: #FF7B72">=</span><span style="color: #C9D1D9"> { </span><span style="color: #FFA657"><data-lsp lsp="(property) id: string">id</data-lsp></span><span style="color: #FF7B72">:</span><span style="color: #C9D1D9"> </span><span style="color: #79C0FF">string</span><span style="color: #C9D1D9">; </span><span style="color: #FFA657"><data-lsp lsp="(property) title: string">title</data-lsp></span><span style="color: #FF7B72">:</span><span style="color: #C9D1D9"> </span><span style="color: #79C0FF">string</span><span style="color: #C9D1D9"> };</span></div><div class="line"><span style="color: #FF7B72">const</span><span style="color: #C9D1D9"> </span><span style="color: #79C0FF"><data-lsp lsp="const posts: Post[]">posts</data-lsp></span><span style="color: #FF7B72">:</span><span style="color: #C9D1D9"> </span><span style="color: #FFA657"><data-lsp lsp="type Post = {
    id: string;
    title: string;
}">Post</data-lsp></span><span style="color: #C9D1D9">[] </span><span style="color: #FF7B72">=</span><span style="color: #C9D1D9"> [];</span></div><div class="line">&nbsp;</div><div class="line"><span style="color: #FF7B72">const</span><span style="color: #C9D1D9"> </span><span style="color: #79C0FF"><data-lsp lsp="const appRouter: BuiltRouter<{
    ctx: object;
    meta: object;
    errorShape: DefaultErrorShape;
    transformer: false;
}, DecorateCreateRouterOptions<{
    post: BuiltRouter<{
        ctx: object;
        meta: object;
        errorShape: DefaultErrorShape;
        transformer: false;
    }, DecorateCreateRouterOptions<{
        byId: QueryProcedure<{
            input: {
                id: string;
            };
            output: Post;
            meta: object;
        }>;
        byTitle: QueryProcedure<{
            input: {
                title: string;
            };
            output: Post;
            meta: object;
        }>;
        create: MutationProcedure<{
            input: {
                title: string;
            };
            output: {
                title: string;
                id: string;
            };
            meta: object;
        }>;
    }>>;
}>>">appRouter</data-lsp></span><span style="color: #C9D1D9"> </span><span style="color: #FF7B72">=</span><span style="color: #C9D1D9"> </span><span style="color: #D2A8FF"><data-lsp lsp="const router: RouterBuilder
<{
    post: BuiltRouter<{
        ctx: object;
        meta: object;
        errorShape: DefaultErrorShape;
        transformer: false;
    }, DecorateCreateRouterOptions<{
        byId: QueryProcedure<{
            input: {
                id: string;
            };
            output: Post;
            meta: object;
        }>;
        byTitle: QueryProcedure<{
            input: {
                title: string;
            };
            output: Post;
            meta: object;
        }>;
        create: MutationProcedure<{
            input: {
                title: string;
            };
            output: {
                title: string;
                id: string;
            };
            meta: object;
        }>;
    }>>;
}>(_: {
    post: BuiltRouter<{
        ctx: object;
        meta: object;
        errorShape: DefaultErrorShape;
        transformer: false;
    }, DecorateCreateRouterOptions<{
        byId: QueryProcedure<{
            input: {
                id: string;
            };
            output: Post;
            meta: object;
        }>;
        byTitle: QueryProcedure<{
            input: {
                title: string;
            };
            output: Post;
            meta: object;
        }>;
        create: MutationProcedure<{
            input: {
                title: string;
            };
            output: {
                title: string;
                id: string;
            };
            meta: object;
        }>;
    }>>;
}) => BuiltRouter<...>">router</data-lsp></span><span style="color: #C9D1D9">({</span></div><div class="line"><span style="color: #C9D1D9">  <data-lsp lsp="(property) post: BuiltRouter<{
    ctx: object;
    meta: object;
    errorShape: DefaultErrorShape;
    transformer: false;
}, DecorateCreateRouterOptions<{
    byId: QueryProcedure<{
        input: {
            id: string;
        };
        output: Post;
        meta: object;
    }>;
    byTitle: QueryProcedure<{
        input: {
            title: string;
        };
        output: Post;
        meta: object;
    }>;
    create: MutationProcedure<{
        input: {
            title: string;
        };
        output: {
            title: string;
            id: string;
        };
        meta: object;
    }>;
}>>">post</data-lsp>: </span><span style="color: #D2A8FF"><data-lsp lsp="const router: RouterBuilder
<{
    byId: QueryProcedure<{
        input: {
            id: string;
        };
        output: Post;
        meta: object;
    }>;
    byTitle: QueryProcedure<{
        input: {
            title: string;
        };
        output: Post;
        meta: object;
    }>;
    create: MutationProcedure<{
        input: {
            title: string;
        };
        output: {
            title: string;
            id: string;
        };
        meta: object;
    }>;
}>(_: {
    byId: QueryProcedure<{
        input: {
            id: string;
        };
        output: Post;
        meta: object;
    }>;
    byTitle: QueryProcedure<{
        input: {
            title: string;
        };
        output: Post;
        meta: object;
    }>;
    create: MutationProcedure<{
        input: {
            title: string;
        };
        output: {
            title: string;
            id: string;
        };
        meta: object;
    }>;
}) => BuiltRouter<...>">router</data-lsp></span><span style="color: #C9D1D9">({</span></div><div class="line"><span style="color: #C9D1D9">    <data-lsp lsp="(property) byId: QueryProcedure<{
    input: {
        id: string;
    };
    output: Post;
    meta: object;
}>">byId</data-lsp>: <data-lsp lsp="const publicProcedure: ProcedureBuilder<object, object, object, UnsetMarker, UnsetMarker, UnsetMarker, UnsetMarker, false>">publicProcedure</data-lsp></span></div><div class="line"><span style="color: #C9D1D9">      .</span><span style="color: #D2A8FF"><data-lsp lsp="(method) ProcedureBuilder<object, object, object, UnsetMarker, UnsetMarker, UnsetMarker, UnsetMarker, false>.input<z.ZodObject<{
    id: z.ZodString;
}, z.core.$strip>>(schema: z.ZodObject<{
    id: z.ZodString;
}, z.core.$strip>): ProcedureBuilder<object, object, object, {
    id: string;
}, {
    id: string;
}, UnsetMarker, UnsetMarker, false>">input</data-lsp></span><span style="color: #C9D1D9">(<data-lsp lsp="import z">z</data-lsp>.</span><span style="color: #D2A8FF"><data-lsp lsp="function object<{
    id: z.ZodString;
}>(shape?: {
    id: z.ZodString;
} | undefined, params?: string | {
    error?: string | z.core.$ZodErrorMap<NonNullable<z.core.$ZodIssueUnrecognizedKeys | z.core.$ZodIssueInvalidType<unknown>>> | undefined;
    message?: string | undefined | undefined;
} | undefined): z.ZodObject<{
    id: z.ZodString;
}, z.core.$strip>">object</data-lsp></span><span style="color: #C9D1D9">({ <data-lsp lsp="(property) id: z.ZodString">id</data-lsp>: <data-lsp lsp="import z">z</data-lsp>.</span><span style="color: #D2A8FF"><data-lsp lsp="function string(params?: string | z.core.$ZodStringParams): z.ZodString (+1 overload)">string</data-lsp></span><span style="color: #C9D1D9">() }))</span></div><div class="line"><span style="color: #C9D1D9">      .</span><span style="color: #D2A8FF"><data-lsp lsp="(method) ProcedureBuilder<object, object, object, { id: string; }, { id: string; }, UnsetMarker, UnsetMarker, false>.query<Post>(resolver: ProcedureResolver<object, object, object, {
    id: string;
}, UnsetMarker, Post>): QueryProcedure<{
    input: {
        id: string;
    };
    output: Post;
    meta: object;
}>">query</data-lsp></span><span style="color: #C9D1D9">(({ </span><span style="color: #FFA657"><data-lsp lsp="(parameter) input: {
    id: string;
}">input</data-lsp></span><span style="color: #C9D1D9"> }) </span><span style="color: #FF7B72">=&gt;</span><span style="color: #C9D1D9"> {</span></div><div class="line"><span style="color: #C9D1D9">        </span><span style="color: #FF7B72">const</span><span style="color: #C9D1D9"> </span><span style="color: #79C0FF"><data-lsp lsp="const post: Post | undefined">post</data-lsp></span><span style="color: #C9D1D9"> </span><span style="color: #FF7B72">=</span><span style="color: #C9D1D9"> <data-lsp lsp="const posts: Post[]">posts</data-lsp>.</span><span style="color: #D2A8FF"><data-lsp lsp="(method) Array<Post>.find(predicate: (value: Post, index: number, obj: Post[]) => unknown, thisArg?: any): Post | undefined (+1 overload)">find</data-lsp></span><span style="color: #C9D1D9">((</span><span style="color: #FFA657"><data-lsp lsp="(parameter) p: Post">p</data-lsp></span><span style="color: #C9D1D9">) </span><span style="color: #FF7B72">=&gt;</span><span style="color: #C9D1D9"> <data-lsp lsp="(parameter) p: Post">p</data-lsp>.<data-lsp lsp="(property) id: string">id</data-lsp> </span><span style="color: #FF7B72">===</span><span style="color: #C9D1D9"> <data-lsp lsp="(parameter) input: {
    id: string;
}">input</data-lsp>.<data-lsp lsp="(property) id: string">id</data-lsp>);</span></div><div class="line"><span style="color: #C9D1D9">        </span><span style="color: #FF7B72">if</span><span style="color: #C9D1D9"> (</span><span style="color: #FF7B72">!</span><span style="color: #C9D1D9"><data-lsp lsp="const post: Post | undefined">post</data-lsp>) </span><span style="color: #FF7B72">throw</span><span style="color: #C9D1D9"> </span><span style="color: #FF7B72">new</span><span style="color: #C9D1D9"> </span><span style="color: #D2A8FF"><data-lsp lsp="(alias) new TRPCError(opts: {
    message?: string;
    code: TRPC_ERROR_CODE_KEY;
    cause?: unknown;
}): TRPCError
import TRPCError">TRPCError</data-lsp></span><span style="color: #C9D1D9">({ <data-lsp lsp="(property) code: &quot;NOT_FOUND&quot; | &quot;PARSE_ERROR&quot; | &quot;BAD_REQUEST&quot; | &quot;INTERNAL_SERVER_ERROR&quot; | &quot;NOT_IMPLEMENTED&quot; | &quot;BAD_GATEWAY&quot; | &quot;SERVICE_UNAVAILABLE&quot; | &quot;GATEWAY_TIMEOUT&quot; | &quot;UNAUTHORIZED&quot; | &quot;PAYMENT_REQUIRED&quot; | &quot;FORBIDDEN&quot; | &quot;METHOD_NOT_SUPPORTED&quot; | &quot;TIMEOUT&quot; | &quot;CONFLICT&quot; | &quot;PRECONDITION_FAILED&quot; | &quot;PAYLOAD_TOO_LARGE&quot; | &quot;UNSUPPORTED_MEDIA_TYPE&quot; | &quot;UNPROCESSABLE_CONTENT&quot; | &quot;PRECONDITION_REQUIRED&quot; | &quot;TOO_MANY_REQUESTS&quot; | &quot;CLIENT_CLOSED_REQUEST&quot;">code</data-lsp>: </span><span style="color: #A5D6FF">"NOT_FOUND"</span><span style="color: #C9D1D9"> });</span></div><div class="line"><span style="color: #C9D1D9">        </span><span style="color: #FF7B72">return</span><span style="color: #C9D1D9"> <data-lsp lsp="const post: Post">post</data-lsp>;</span></div><div class="line"><span style="color: #C9D1D9">      }),</span></div><div class="line"><span style="color: #C9D1D9">    <data-lsp lsp="(property) byTitle: QueryProcedure<{
    input: {
        title: string;
    };
    output: Post;
    meta: object;
}>">byTitle</data-lsp>: <data-lsp lsp="const publicProcedure: ProcedureBuilder<object, object, object, UnsetMarker, UnsetMarker, UnsetMarker, UnsetMarker, false>">publicProcedure</data-lsp></span></div><div class="line"><span style="color: #C9D1D9">      .</span><span style="color: #D2A8FF"><data-lsp lsp="(method) ProcedureBuilder<object, object, object, UnsetMarker, UnsetMarker, UnsetMarker, UnsetMarker, false>.input<z.ZodObject<{
    title: z.ZodString;
}, z.core.$strip>>(schema: z.ZodObject<{
    title: z.ZodString;
}, z.core.$strip>): ProcedureBuilder<object, object, object, {
    title: string;
}, {
    title: string;
}, UnsetMarker, UnsetMarker, false>">input</data-lsp></span><span style="color: #C9D1D9">(<data-lsp lsp="import z">z</data-lsp>.</span><span style="color: #D2A8FF"><data-lsp lsp="function object<{
    title: z.ZodString;
}>(shape?: {
    title: z.ZodString;
} | undefined, params?: string | {
    error?: string | z.core.$ZodErrorMap<NonNullable<z.core.$ZodIssueUnrecognizedKeys | z.core.$ZodIssueInvalidType<unknown>>> | undefined;
    message?: string | undefined | undefined;
} | undefined): z.ZodObject<{
    title: z.ZodString;
}, z.core.$strip>">object</data-lsp></span><span style="color: #C9D1D9">({ <data-lsp lsp="(property) title: z.ZodString">title</data-lsp>: <data-lsp lsp="import z">z</data-lsp>.</span><span style="color: #D2A8FF"><data-lsp lsp="function string(params?: string | z.core.$ZodStringParams): z.ZodString (+1 overload)">string</data-lsp></span><span style="color: #C9D1D9">() }))</span></div><div class="line"><span style="color: #C9D1D9">      .</span><span style="color: #D2A8FF"><data-lsp lsp="(method) ProcedureBuilder<object, object, object, { title: string; }, { title: string; }, UnsetMarker, UnsetMarker, false>.query<Post>(resolver: ProcedureResolver<object, object, object, {
    title: string;
}, UnsetMarker, Post>): QueryProcedure<{
    input: {
        title: string;
    };
    output: Post;
    meta: object;
}>">query</data-lsp></span><span style="color: #C9D1D9">(({ </span><span style="color: #FFA657"><data-lsp lsp="(parameter) input: {
    title: string;
}">input</data-lsp></span><span style="color: #C9D1D9"> }) </span><span style="color: #FF7B72">=&gt;</span><span style="color: #C9D1D9"> {</span></div><div class="line"><span style="color: #C9D1D9">        </span><span style="color: #FF7B72">const</span><span style="color: #C9D1D9"> </span><span style="color: #79C0FF"><data-lsp lsp="const post: Post | undefined">post</data-lsp></span><span style="color: #C9D1D9"> </span><span style="color: #FF7B72">=</span><span style="color: #C9D1D9"> <data-lsp lsp="const posts: Post[]">posts</data-lsp>.</span><span style="color: #D2A8FF"><data-lsp lsp="(method) Array<Post>.find(predicate: (value: Post, index: number, obj: Post[]) => unknown, thisArg?: any): Post | undefined (+1 overload)">find</data-lsp></span><span style="color: #C9D1D9">((</span><span style="color: #FFA657"><data-lsp lsp="(parameter) p: Post">p</data-lsp></span><span style="color: #C9D1D9">) </span><span style="color: #FF7B72">=&gt;</span><span style="color: #C9D1D9"> <data-lsp lsp="(parameter) p: Post">p</data-lsp>.<data-lsp lsp="(property) title: string">title</data-lsp> </span><span style="color: #FF7B72">===</span><span style="color: #C9D1D9"> <data-lsp lsp="(parameter) input: {
    title: string;
}">input</data-lsp>.<data-lsp lsp="(property) title: string">title</data-lsp>);</span></div><div class="line"><span style="color: #C9D1D9">        </span><span style="color: #FF7B72">if</span><span style="color: #C9D1D9"> (</span><span style="color: #FF7B72">!</span><span style="color: #C9D1D9"><data-lsp lsp="const post: Post | undefined">post</data-lsp>) </span><span style="color: #FF7B72">throw</span><span style="color: #C9D1D9"> </span><span style="color: #FF7B72">new</span><span style="color: #C9D1D9"> </span><span style="color: #D2A8FF"><data-lsp lsp="(alias) new TRPCError(opts: {
    message?: string;
    code: TRPC_ERROR_CODE_KEY;
    cause?: unknown;
}): TRPCError
import TRPCError">TRPCError</data-lsp></span><span style="color: #C9D1D9">({ <data-lsp lsp="(property) code: &quot;NOT_FOUND&quot; | &quot;PARSE_ERROR&quot; | &quot;BAD_REQUEST&quot; | &quot;INTERNAL_SERVER_ERROR&quot; | &quot;NOT_IMPLEMENTED&quot; | &quot;BAD_GATEWAY&quot; | &quot;SERVICE_UNAVAILABLE&quot; | &quot;GATEWAY_TIMEOUT&quot; | &quot;UNAUTHORIZED&quot; | &quot;PAYMENT_REQUIRED&quot; | &quot;FORBIDDEN&quot; | &quot;METHOD_NOT_SUPPORTED&quot; | &quot;TIMEOUT&quot; | &quot;CONFLICT&quot; | &quot;PRECONDITION_FAILED&quot; | &quot;PAYLOAD_TOO_LARGE&quot; | &quot;UNSUPPORTED_MEDIA_TYPE&quot; | &quot;UNPROCESSABLE_CONTENT&quot; | &quot;PRECONDITION_REQUIRED&quot; | &quot;TOO_MANY_REQUESTS&quot; | &quot;CLIENT_CLOSED_REQUEST&quot;">code</data-lsp>: </span><span style="color: #A5D6FF">"NOT_FOUND"</span><span style="color: #C9D1D9"> });</span></div><div class="line"><span style="color: #C9D1D9">        </span><span style="color: #FF7B72">return</span><span style="color: #C9D1D9"> <data-lsp lsp="const post: Post">post</data-lsp>;</span></div><div class="line"><span style="color: #C9D1D9">      }),</span></div><div class="line"><span style="color: #C9D1D9">    <data-lsp lsp="(property) create: MutationProcedure<{
    input: {
        title: string;
    };
    output: {
        title: string;
        id: string;
    };
    meta: object;
}>">create</data-lsp>: <data-lsp lsp="const publicProcedure: ProcedureBuilder<object, object, object, UnsetMarker, UnsetMarker, UnsetMarker, UnsetMarker, false>">publicProcedure</data-lsp></span></div><div class="line"><span style="color: #C9D1D9">      .</span><span style="color: #D2A8FF"><data-lsp lsp="(method) ProcedureBuilder<object, object, object, UnsetMarker, UnsetMarker, UnsetMarker, UnsetMarker, false>.input<z.ZodObject<{
    title: z.ZodString;
}, z.core.$strip>>(schema: z.ZodObject<{
    title: z.ZodString;
}, z.core.$strip>): ProcedureBuilder<object, object, object, {
    title: string;
}, {
    title: string;
}, UnsetMarker, UnsetMarker, false>">input</data-lsp></span><span style="color: #C9D1D9">(<data-lsp lsp="import z">z</data-lsp>.</span><span style="color: #D2A8FF"><data-lsp lsp="function object<{
    title: z.ZodString;
}>(shape?: {
    title: z.ZodString;
} | undefined, params?: string | {
    error?: string | z.core.$ZodErrorMap<NonNullable<z.core.$ZodIssueUnrecognizedKeys | z.core.$ZodIssueInvalidType<unknown>>> | undefined;
    message?: string | undefined | undefined;
} | undefined): z.ZodObject<{
    title: z.ZodString;
}, z.core.$strip>">object</data-lsp></span><span style="color: #C9D1D9">({ <data-lsp lsp="(property) title: z.ZodString">title</data-lsp>: <data-lsp lsp="import z">z</data-lsp>.</span><span style="color: #D2A8FF"><data-lsp lsp="function string(params?: string | z.core.$ZodStringParams): z.ZodString (+1 overload)">string</data-lsp></span><span style="color: #C9D1D9">() }))</span></div><div class="line"><span style="color: #C9D1D9">      .</span><span style="color: #D2A8FF"><data-lsp lsp="(method) ProcedureBuilder<object, object, object, { title: string; }, { title: string; }, UnsetMarker, UnsetMarker, false>.mutation<{
    title: string;
    id: string;
}>(resolver: ProcedureResolver<object, object, object, {
    title: string;
}, UnsetMarker, {
    title: string;
    id: string;
}>): MutationProcedure<{
    input: {
        title: string;
    };
    output: {
        title: string;
        id: string;
    };
    meta: object;
}>">mutation</data-lsp></span><span style="color: #C9D1D9">(({ </span><span style="color: #FFA657"><data-lsp lsp="(parameter) input: {
    title: string;
}">input</data-lsp></span><span style="color: #C9D1D9"> }) </span><span style="color: #FF7B72">=&gt;</span><span style="color: #C9D1D9"> {</span></div><div class="line"><span style="color: #C9D1D9">        </span><span style="color: #FF7B72">const</span><span style="color: #C9D1D9"> </span><span style="color: #79C0FF"><data-lsp lsp="const post: {
    title: string;
    id: string;
}">post</data-lsp></span><span style="color: #C9D1D9"> </span><span style="color: #FF7B72">=</span><span style="color: #C9D1D9"> { <data-lsp lsp="(property) id: string">id</data-lsp>: </span><span style="color: #D2A8FF"><data-lsp lsp="const uuid: () => string">uuid</data-lsp></span><span style="color: #C9D1D9">(), </span><span style="color: #FF7B72">...</span><span style="color: #C9D1D9"><data-lsp lsp="(parameter) input: {
    title: string;
}">input</data-lsp> };</span></div><div class="line"><span style="color: #C9D1D9">        <data-lsp lsp="const posts: Post[]">posts</data-lsp>.</span><span style="color: #D2A8FF"><data-lsp lsp="(method) Array<Post>.push(...items: Post[]): number">push</data-lsp></span><span style="color: #C9D1D9">(<data-lsp lsp="const post: {
    title: string;
    id: string;
}">post</data-lsp>);</span></div><div class="line"><span style="color: #C9D1D9">        </span><span style="color: #FF7B72">return</span><span style="color: #C9D1D9"> <data-lsp lsp="const post: {
    title: string;
    id: string;
}">post</data-lsp>;</span></div><div class="line"><span style="color: #C9D1D9">      }),</span></div><div class="line"><span style="color: #C9D1D9">  }),</span></div><div class="line"><span style="color: #C9D1D9">});</span></div></code></div><button type="button" aria-label="Copy code to clipboard" class="copy-button" onclick="navigator.clipboard.writeText(this.previousSibling.innerText),this.classList.add(&quot;copied&quot;),this.textContent=&quot;Copied&quot;,setTimeout(()=>{this.classList.remove(&quot;copied&quot;),this.textContent=&quot;Copy&quot;},2e3)">Copy</button></pre></div>
<p>Målet med vår klient är att efterlikna denna objektstruktur på klientsidan så att vi kan anropa procedurer så här:</p>
<div><pre class="shiki min-light" style="background-color: #ffffff; color: #24292eff"><div class="language-id">ts</div><div class="code-container"><code><div class="line"><span style="color: #D32F2F">const</span><span style="color: #24292EFF"> </span><span style="color: #1976D2">post1</span><span style="color: #24292EFF"> </span><span style="color: #D32F2F">=</span><span style="color: #24292EFF"> </span><span style="color: #D32F2F">await</span><span style="color: #24292EFF"> </span><span style="color: #1976D2">client</span><span style="color: #6F42C1">.</span><span style="color: #1976D2">post</span><span style="color: #6F42C1">.</span><span style="color: #1976D2">byId</span><span style="color: #6F42C1">.query</span><span style="color: #24292EFF">({ id</span><span style="color: #D32F2F">:</span><span style="color: #24292EFF"> </span><span style="color: #22863A">'123'</span><span style="color: #24292EFF"> });</span></div><div class="line"><span style="color: #D32F2F">const</span><span style="color: #24292EFF"> </span><span style="color: #1976D2">post2</span><span style="color: #24292EFF"> </span><span style="color: #D32F2F">=</span><span style="color: #24292EFF"> </span><span style="color: #D32F2F">await</span><span style="color: #24292EFF"> </span><span style="color: #1976D2">client</span><span style="color: #6F42C1">.</span><span style="color: #1976D2">post</span><span style="color: #6F42C1">.</span><span style="color: #1976D2">byTitle</span><span style="color: #6F42C1">.query</span><span style="color: #24292EFF">({ title</span><span style="color: #D32F2F">:</span><span style="color: #24292EFF"> </span><span style="color: #22863A">'Hello world'</span><span style="color: #24292EFF"> });</span></div><div class="line"><span style="color: #D32F2F">const</span><span style="color: #24292EFF"> </span><span style="color: #1976D2">newPost</span><span style="color: #24292EFF"> </span><span style="color: #D32F2F">=</span><span style="color: #24292EFF"> </span><span style="color: #D32F2F">await</span><span style="color: #24292EFF"> </span><span style="color: #1976D2">client</span><span style="color: #6F42C1">.</span><span style="color: #1976D2">post</span><span style="color: #6F42C1">.</span><span style="color: #1976D2">create</span><span style="color: #6F42C1">.mutate</span><span style="color: #24292EFF">({ title</span><span style="color: #D32F2F">:</span><span style="color: #24292EFF"> </span><span style="color: #22863A">'Foo'</span><span style="color: #24292EFF"> });</span></div></code></div><button type="button" aria-label="Copy code to clipboard" class="copy-button" onclick="navigator.clipboard.writeText(this.previousSibling.innerText),this.classList.add(&quot;copied&quot;),this.textContent=&quot;Copied&quot;,setTimeout(()=>{this.classList.remove(&quot;copied&quot;),this.textContent=&quot;Copy&quot;},2e3)">Copy</button></pre>
<pre class="shiki github-dark" style="background-color: #0d1117; color: #c9d1d9"><div class="language-id">ts</div><div class="code-container"><code><div class="line"><span style="color: #FF7B72">const</span><span style="color: #C9D1D9"> </span><span style="color: #79C0FF">post1</span><span style="color: #C9D1D9"> </span><span style="color: #FF7B72">=</span><span style="color: #C9D1D9"> </span><span style="color: #FF7B72">await</span><span style="color: #C9D1D9"> client.post.byId.</span><span style="color: #D2A8FF">query</span><span style="color: #C9D1D9">({ id: </span><span style="color: #A5D6FF">'123'</span><span style="color: #C9D1D9"> });</span></div><div class="line"><span style="color: #FF7B72">const</span><span style="color: #C9D1D9"> </span><span style="color: #79C0FF">post2</span><span style="color: #C9D1D9"> </span><span style="color: #FF7B72">=</span><span style="color: #C9D1D9"> </span><span style="color: #FF7B72">await</span><span style="color: #C9D1D9"> client.post.byTitle.</span><span style="color: #D2A8FF">query</span><span style="color: #C9D1D9">({ title: </span><span style="color: #A5D6FF">'Hello world'</span><span style="color: #C9D1D9"> });</span></div><div class="line"><span style="color: #FF7B72">const</span><span style="color: #C9D1D9"> </span><span style="color: #79C0FF">newPost</span><span style="color: #C9D1D9"> </span><span style="color: #FF7B72">=</span><span style="color: #C9D1D9"> </span><span style="color: #FF7B72">await</span><span style="color: #C9D1D9"> client.post.create.</span><span style="color: #D2A8FF">mutate</span><span style="color: #C9D1D9">({ title: </span><span style="color: #A5D6FF">'Foo'</span><span style="color: #C9D1D9"> });</span></div></code></div><button type="button" aria-label="Copy code to clipboard" class="copy-button" onclick="navigator.clipboard.writeText(this.previousSibling.innerText),this.classList.add(&quot;copied&quot;),this.textContent=&quot;Copied&quot;,setTimeout(()=>{this.classList.remove(&quot;copied&quot;),this.textContent=&quot;Copy&quot;},2e3)">Copy</button></pre></div>
<p>För att uppnå detta använder tRPC en kombination av <a href="https://mdn.io/proxy" target="_blank" rel="noopener noreferrer"><code>Proxy</code></a>-objekt och TypeScript-magik för att utöka objektstrukturen med <code>.query</code> och <code>.mutate</code>-metoderna - vilket innebär att vi faktiskt LJUGR för dig om vad du gör (mer om detta senare) för att ge en fantastisk utvecklarupplevelse!</p>
<p>På en hög nivå vill vi mappa <code>post.byId.query()</code> till en GET-förfrågan till vår server, och <code>post.create.mutate()</code> till en POST-förfrågan, samtidigt som typerna propageras från backend till frontend. Så, hur gör vi detta?</p>
<h2 class="anchor anchorWithStickyNavbar_MYIC" id="implementera-en-minimal-trpc-klient">Implementera en minimal tRPC-klient<a href="https://trpc.io/sv/blog/tinyrpc-client#implementera-en-minimal-trpc-klient" class="hash-link" aria-label="Direktlänk till Implementera en minimal tRPC-klient" title="Direktlänk till Implementera en minimal tRPC-klient">​</a></h2>
<h3 class="anchor anchorWithStickyNavbar_MYIC" id="️-typescript-magirin">🧙‍♀️ TypeScript-magirin<a href="https://trpc.io/sv/blog/tinyrpc-client#%EF%B8%8F-typescript-magirin" class="hash-link" aria-label="Direktlänk till 🧙‍♀️ TypeScript-magirin" title="Direktlänk till 🧙‍♀️ TypeScript-magirin">​</a></h3>
<p>Låt oss börja med den roliga TypeScript-magirin för att låsa upp den fantastiska autofyllningen och typsäkerheten vi alla känner och älskar från att använda tRPC.</p>
<p>Vi behöver använda rekursiva typer för att kunna inferera godtyckligt djupa routerstrukturer. Vi vet också att vi vill att våra procedurer <code>post.byId</code> och <code>post.create</code> ska ha <code>.query</code> respektive <code>.mutate</code>-metoder - i tRPC kallar vi detta för att dekorera procedurerna. I <code>@trpc/server</code> har vi några inferenshjälpare som infererar input- och outputtyper för våra procedurer med dessa metoder, vilket vi kommer använda för att inferera typerna för dessa funktioner - så låt oss skriva lite kod!</p>
<p>Låt oss överväga vad vi vill uppnå för att tillhandahålla autofyllning av sökvägar samt inferens av procedurernas input- och outputtyper:</p>
<ul>
<li>
<p>Om vi befinner oss på en router vill vi kunna komma åt dess underrouter och procedurer (vi återkommer till detta strax)</p>
</li>
<li>
<p>Om vi befinner oss på en query-procedure vill vi kunna anropa <code>.query</code> på den</p>
</li>
<li>
<p>Om vi befinner oss på en mutation-procedure vill vi kunna anropa <code>.mutate</code> på den</p>
</li>
<li>
<p>Om vi försöker komma åt något annat vill vi få ett typfel som indikerar att proceduren inte finns på servern</p>
</li>
</ul>
<p>Så låt oss skapa en typ som gör detta åt oss:</p>
<div></div>
<div></div>
<div><pre class="shiki min-light twoslash lsp" style="background-color: #ffffff; color: #24292eff"><div class="language-id">ts</div><div class="code-container"><code><div class="line"><span style="color: #D32F2F">type</span><span style="color: #24292EFF"> </span><span style="color: #6F42C1"><data-lsp lsp="type DecorateProcedure<TProcedure> = TProcedure extends AnyTRPCQueryProcedure ? {
    query: Resolver<TProcedure>;
} : TProcedure extends AnyTRPCMutationProcedure ? {
    mutate: Resolver<TProcedure>;
} : never">DecorateProcedure</data-lsp></span><span style="color: #24292EFF">&lt;</span><span style="color: #6F42C1"><data-lsp lsp="(type parameter) TProcedure in type DecorateProcedure<TProcedure>">TProcedure</data-lsp></span><span style="color: #24292EFF">&gt; </span><span style="color: #D32F2F">=</span><span style="color: #24292EFF"> </span><span style="color: #6F42C1"><data-lsp lsp="(type parameter) TProcedure in type DecorateProcedure<TProcedure>">TProcedure</data-lsp></span><span style="color: #24292EFF"> </span><span style="color: #D32F2F">extends</span><span style="color: #24292EFF"> </span><span style="color: #6F42C1"><data-lsp lsp="(alias) type AnyTRPCQueryProcedure = QueryProcedure<any>
import AnyTRPCQueryProcedure">AnyTRPCQueryProcedure</data-lsp></span></div><div class="line"><span style="color: #24292EFF">  </span><span style="color: #D32F2F">?</span><span style="color: #24292EFF"> {</span></div><div class="line"><span style="color: #24292EFF">      <data-lsp lsp="(property) query: Resolver<TProcedure>">query</data-lsp></span><span style="color: #D32F2F">:</span><span style="color: #24292EFF"> </span><span style="color: #6F42C1"><data-lsp lsp="type Resolver<TProcedure extends AnyTRPCProcedure> = (input: inferProcedureInput<TProcedure>) => Promise<inferProcedureOutput<TProcedure>>">Resolver</data-lsp></span><span style="color: #24292EFF">&lt;</span><span style="color: #6F42C1"><data-lsp lsp="(type parameter) TProcedure in type DecorateProcedure<TProcedure>">TProcedure</data-lsp></span><span style="color: #24292EFF">&gt;;</span></div><div class="line"><span style="color: #24292EFF">    }</span></div><div class="line"><span style="color: #24292EFF">  </span><span style="color: #D32F2F">:</span><span style="color: #24292EFF"> </span><span style="color: #6F42C1"><data-lsp lsp="(type parameter) TProcedure in type DecorateProcedure<TProcedure>">TProcedure</data-lsp></span><span style="color: #24292EFF"> </span><span style="color: #D32F2F">extends</span><span style="color: #24292EFF"> </span><span style="color: #6F42C1"><data-lsp lsp="(alias) type AnyTRPCMutationProcedure = MutationProcedure<any>
import AnyTRPCMutationProcedure">AnyTRPCMutationProcedure</data-lsp></span></div><div class="line"><span style="color: #24292EFF">  </span><span style="color: #D32F2F">?</span><span style="color: #24292EFF"> {</span></div><div class="line"><span style="color: #24292EFF">      <data-lsp lsp="(property) mutate: Resolver<TProcedure>">mutate</data-lsp></span><span style="color: #D32F2F">:</span><span style="color: #24292EFF"> </span><span style="color: #6F42C1"><data-lsp lsp="type Resolver<TProcedure extends AnyTRPCProcedure> = (input: inferProcedureInput<TProcedure>) => Promise<inferProcedureOutput<TProcedure>>">Resolver</data-lsp></span><span style="color: #24292EFF">&lt;</span><span style="color: #6F42C1"><data-lsp lsp="(type parameter) TProcedure in type DecorateProcedure<TProcedure>">TProcedure</data-lsp></span><span style="color: #24292EFF">&gt;;</span></div><div class="line"><span style="color: #24292EFF">    }</span></div><div class="line"><span style="color: #24292EFF">  </span><span style="color: #D32F2F">:</span><span style="color: #24292EFF"> </span><span style="color: #1976D2">never</span><span style="color: #24292EFF">;</span></div></code></div><button type="button" aria-label="Copy code to clipboard" class="copy-button" onclick="navigator.clipboard.writeText(this.previousSibling.innerText),this.classList.add(&quot;copied&quot;),this.textContent=&quot;Copied&quot;,setTimeout(()=>{this.classList.remove(&quot;copied&quot;),this.textContent=&quot;Copy&quot;},2e3)">Copy</button></pre>
<pre class="shiki github-dark twoslash lsp" style="background-color: #0d1117; color: #c9d1d9"><div class="language-id">ts</div><div class="code-container"><code><div class="line"><span style="color: #FF7B72">type</span><span style="color: #C9D1D9"> </span><span style="color: #FFA657"><data-lsp lsp="type DecorateProcedure<TProcedure> = TProcedure extends AnyTRPCQueryProcedure ? {
    query: Resolver<TProcedure>;
} : TProcedure extends AnyTRPCMutationProcedure ? {
    mutate: Resolver<TProcedure>;
} : never">DecorateProcedure</data-lsp></span><span style="color: #C9D1D9">&lt;</span><span style="color: #FFA657"><data-lsp lsp="(type parameter) TProcedure in type DecorateProcedure<TProcedure>">TProcedure</data-lsp></span><span style="color: #C9D1D9">&gt; </span><span style="color: #FF7B72">=</span><span style="color: #C9D1D9"> </span><span style="color: #FFA657"><data-lsp lsp="(type parameter) TProcedure in type DecorateProcedure<TProcedure>">TProcedure</data-lsp></span><span style="color: #C9D1D9"> </span><span style="color: #FF7B72">extends</span><span style="color: #C9D1D9"> </span><span style="color: #FFA657"><data-lsp lsp="(alias) type AnyTRPCQueryProcedure = QueryProcedure<any>
import AnyTRPCQueryProcedure">AnyTRPCQueryProcedure</data-lsp></span></div><div class="line"><span style="color: #C9D1D9">  </span><span style="color: #FF7B72">?</span><span style="color: #C9D1D9"> {</span></div><div class="line"><span style="color: #C9D1D9">      </span><span style="color: #FFA657"><data-lsp lsp="(property) query: Resolver<TProcedure>">query</data-lsp></span><span style="color: #FF7B72">:</span><span style="color: #C9D1D9"> </span><span style="color: #FFA657"><data-lsp lsp="type Resolver<TProcedure extends AnyTRPCProcedure> = (input: inferProcedureInput<TProcedure>) => Promise<inferProcedureOutput<TProcedure>>">Resolver</data-lsp></span><span style="color: #C9D1D9">&lt;</span><span style="color: #FFA657"><data-lsp lsp="(type parameter) TProcedure in type DecorateProcedure<TProcedure>">TProcedure</data-lsp></span><span style="color: #C9D1D9">&gt;;</span></div><div class="line"><span style="color: #C9D1D9">    }</span></div><div class="line"><span style="color: #C9D1D9">  </span><span style="color: #FF7B72">:</span><span style="color: #C9D1D9"> </span><span style="color: #FFA657"><data-lsp lsp="(type parameter) TProcedure in type DecorateProcedure<TProcedure>">TProcedure</data-lsp></span><span style="color: #C9D1D9"> </span><span style="color: #FF7B72">extends</span><span style="color: #C9D1D9"> </span><span style="color: #FFA657"><data-lsp lsp="(alias) type AnyTRPCMutationProcedure = MutationProcedure<any>
import AnyTRPCMutationProcedure">AnyTRPCMutationProcedure</data-lsp></span></div><div class="line"><span style="color: #C9D1D9">  </span><span style="color: #FF7B72">?</span><span style="color: #C9D1D9"> {</span></div><div class="line"><span style="color: #C9D1D9">      </span><span style="color: #FFA657"><data-lsp lsp="(property) mutate: Resolver<TProcedure>">mutate</data-lsp></span><span style="color: #FF7B72">:</span><span style="color: #C9D1D9"> </span><span style="color: #FFA657"><data-lsp lsp="type Resolver<TProcedure extends AnyTRPCProcedure> = (input: inferProcedureInput<TProcedure>) => Promise<inferProcedureOutput<TProcedure>>">Resolver</data-lsp></span><span style="color: #C9D1D9">&lt;</span><span style="color: #FFA657"><data-lsp lsp="(type parameter) TProcedure in type DecorateProcedure<TProcedure>">TProcedure</data-lsp></span><span style="color: #C9D1D9">&gt;;</span></div><div class="line"><span style="color: #C9D1D9">    }</span></div><div class="line"><span style="color: #C9D1D9">  </span><span style="color: #FF7B72">:</span><span style="color: #C9D1D9"> </span><span style="color: #79C0FF">never</span><span style="color: #C9D1D9">;</span></div></code></div><button type="button" aria-label="Copy code to clipboard" class="copy-button" onclick="navigator.clipboard.writeText(this.previousSibling.innerText),this.classList.add(&quot;copied&quot;),this.textContent=&quot;Copied&quot;,setTimeout(()=>{this.classList.remove(&quot;copied&quot;),this.textContent=&quot;Copy&quot;},2e3)">Copy</button></pre></div>
<p>Vi kommer använda några av tRPC<!-- -->:s<!-- --> inbyggda inferenshjälpare för att inferera input- och outputtyperna för våra procedurer när vi definierar <code>Resolver</code>-typen.</p>
<div><pre class="shiki min-light twoslash lsp" style="background-color: #ffffff; color: #24292eff"><div class="language-id">ts</div><div class="code-container"><code><div class="line"><span style="color: #D32F2F">import</span><span style="color: #24292EFF"> </span><span style="color: #D32F2F">type</span><span style="color: #24292EFF"> {</span></div><div class="line"><span style="color: #24292EFF">  <data-lsp lsp="(alias) type AnyTRPCProcedure = AnyTRPCQueryProcedure | AnyTRPCMutationProcedure | AnySubscriptionProcedure
import AnyTRPCProcedure">AnyTRPCProcedure</data-lsp></span><span style="color: #212121">,</span></div><div class="line"><span style="color: #24292EFF">  <data-lsp lsp="(alias) type inferProcedureInput<TProcedure extends AnyProcedure> = undefined extends inferProcedureParams<TProcedure>[&quot;$types&quot;][&quot;input&quot;] ? void | inferProcedureParams<TProcedure>[&quot;$types&quot;][&quot;input&quot;] : inferProcedureParams<TProcedure>[&quot;$types&quot;][&quot;input&quot;]
import inferProcedureInput">inferProcedureInput</data-lsp></span><span style="color: #212121">,</span></div><div class="line"><span style="color: #24292EFF">  <data-lsp lsp="(alias) type inferProcedureOutput<TProcedure> = inferProcedureParams<TProcedure>[&quot;$types&quot;][&quot;output&quot;]
import inferProcedureOutput">inferProcedureOutput</data-lsp></span><span style="color: #212121">,</span></div><div class="line"><span style="color: #24292EFF">  <data-lsp lsp="(alias) type AnyTRPCQueryProcedure = QueryProcedure<any>
import AnyTRPCQueryProcedure">AnyTRPCQueryProcedure</data-lsp></span><span style="color: #212121">,</span></div><div class="line"><span style="color: #24292EFF">  <data-lsp lsp="(alias) type AnyTRPCMutationProcedure = MutationProcedure<any>
import AnyTRPCMutationProcedure">AnyTRPCMutationProcedure</data-lsp></span></div><div class="line"><span style="color: #24292EFF">} </span><span style="color: #D32F2F">from</span><span style="color: #24292EFF"> </span><span style="color: #22863A">'@trpc/server'</span><span style="color: #24292EFF">;</span></div><div class="line">&nbsp;</div><div class="line">&nbsp;</div><div class="line">&nbsp;</div><div class="line"><span style="color: #D32F2F">type</span><span style="color: #24292EFF"> </span><span style="color: #6F42C1"><data-lsp lsp="type Resolver<TProcedure extends AnyTRPCProcedure> = (input: inferProcedureInput<TProcedure>) => Promise<inferProcedureOutput<TProcedure>>">Resolver</data-lsp></span><span style="color: #24292EFF">&lt;</span><span style="color: #6F42C1"><data-lsp lsp="(type parameter) TProcedure in type Resolver<TProcedure extends AnyTRPCProcedure>">TProcedure</data-lsp></span><span style="color: #24292EFF"> </span><span style="color: #D32F2F">extends</span><span style="color: #24292EFF"> </span><span style="color: #6F42C1"><data-lsp lsp="(alias) type AnyTRPCProcedure = AnyTRPCQueryProcedure | AnyTRPCMutationProcedure | AnySubscriptionProcedure
import AnyTRPCProcedure">AnyTRPCProcedure</data-lsp></span><span style="color: #24292EFF">&gt; </span><span style="color: #D32F2F">=</span><span style="color: #24292EFF"> (</span></div><div class="line"><span style="color: #24292EFF">  <data-lsp lsp="(parameter) input: inferProcedureInput<TProcedure>">input</data-lsp></span><span style="color: #D32F2F">:</span><span style="color: #24292EFF"> </span><span style="color: #6F42C1"><data-lsp lsp="(alias) type inferProcedureInput<TProcedure extends AnyProcedure> = undefined extends inferProcedureParams<TProcedure>[&quot;$types&quot;][&quot;input&quot;] ? void | inferProcedureParams<TProcedure>[&quot;$types&quot;][&quot;input&quot;] : inferProcedureParams<TProcedure>[&quot;$types&quot;][&quot;input&quot;]
import inferProcedureInput">inferProcedureInput</data-lsp></span><span style="color: #24292EFF">&lt;</span><span style="color: #6F42C1"><data-lsp lsp="(type parameter) TProcedure in type Resolver<TProcedure extends AnyTRPCProcedure>">TProcedure</data-lsp></span><span style="color: #24292EFF">&gt;</span><span style="color: #212121">,</span></div><div class="line"><span style="color: #24292EFF">) </span><span style="color: #D32F2F">=&gt;</span><span style="color: #24292EFF"> </span><span style="color: #6F42C1"><data-lsp lsp="interface Promise<T>">Promise</data-lsp></span><span style="color: #24292EFF">&lt;</span><span style="color: #6F42C1"><data-lsp lsp="(alias) type inferProcedureOutput<TProcedure> = inferProcedureParams<TProcedure>[&quot;$types&quot;][&quot;output&quot;]
import inferProcedureOutput">inferProcedureOutput</data-lsp></span><span style="color: #24292EFF">&lt;</span><span style="color: #6F42C1"><data-lsp lsp="(type parameter) TProcedure in type Resolver<TProcedure extends AnyTRPCProcedure>">TProcedure</data-lsp></span><span style="color: #24292EFF">&gt;&gt;;</span></div><div class="line">&nbsp;</div></code></div><button type="button" aria-label="Copy code to clipboard" class="copy-button" onclick="navigator.clipboard.writeText(this.previousSibling.innerText),this.classList.add(&quot;copied&quot;),this.textContent=&quot;Copied&quot;,setTimeout(()=>{this.classList.remove(&quot;copied&quot;),this.textContent=&quot;Copy&quot;},2e3)">Copy</button></pre>
<pre class="shiki github-dark twoslash lsp" style="background-color: #0d1117; color: #c9d1d9"><div class="language-id">ts</div><div class="code-container"><code><div class="line"><span style="color: #FF7B72">import</span><span style="color: #C9D1D9"> </span><span style="color: #FF7B72">type</span><span style="color: #C9D1D9"> {</span></div><div class="line"><span style="color: #C9D1D9">  <data-lsp lsp="(alias) type AnyTRPCProcedure = AnyTRPCQueryProcedure | AnyTRPCMutationProcedure | AnySubscriptionProcedure
import AnyTRPCProcedure">AnyTRPCProcedure</data-lsp>,</span></div><div class="line"><span style="color: #C9D1D9">  <data-lsp lsp="(alias) type inferProcedureInput<TProcedure extends AnyProcedure> = undefined extends inferProcedureParams<TProcedure>[&quot;$types&quot;][&quot;input&quot;] ? void | inferProcedureParams<TProcedure>[&quot;$types&quot;][&quot;input&quot;] : inferProcedureParams<TProcedure>[&quot;$types&quot;][&quot;input&quot;]
import inferProcedureInput">inferProcedureInput</data-lsp>,</span></div><div class="line"><span style="color: #C9D1D9">  <data-lsp lsp="(alias) type inferProcedureOutput<TProcedure> = inferProcedureParams<TProcedure>[&quot;$types&quot;][&quot;output&quot;]
import inferProcedureOutput">inferProcedureOutput</data-lsp>,</span></div><div class="line"><span style="color: #C9D1D9">  <data-lsp lsp="(alias) type AnyTRPCQueryProcedure = QueryProcedure<any>
import AnyTRPCQueryProcedure">AnyTRPCQueryProcedure</data-lsp>,</span></div><div class="line"><span style="color: #C9D1D9">  <data-lsp lsp="(alias) type AnyTRPCMutationProcedure = MutationProcedure<any>
import AnyTRPCMutationProcedure">AnyTRPCMutationProcedure</data-lsp></span></div><div class="line"><span style="color: #C9D1D9">} </span><span style="color: #FF7B72">from</span><span style="color: #C9D1D9"> </span><span style="color: #A5D6FF">'@trpc/server'</span><span style="color: #C9D1D9">;</span></div><div class="line">&nbsp;</div><div class="line">&nbsp;</div><div class="line">&nbsp;</div><div class="line"><span style="color: #FF7B72">type</span><span style="color: #C9D1D9"> </span><span style="color: #FFA657"><data-lsp lsp="type Resolver<TProcedure extends AnyTRPCProcedure> = (input: inferProcedureInput<TProcedure>) => Promise<inferProcedureOutput<TProcedure>>">Resolver</data-lsp></span><span style="color: #C9D1D9">&lt;</span><span style="color: #FFA657"><data-lsp lsp="(type parameter) TProcedure in type Resolver<TProcedure extends AnyTRPCProcedure>">TProcedure</data-lsp></span><span style="color: #C9D1D9"> </span><span style="color: #FF7B72">extends</span><span style="color: #C9D1D9"> </span><span style="color: #FFA657"><data-lsp lsp="(alias) type AnyTRPCProcedure = AnyTRPCQueryProcedure | AnyTRPCMutationProcedure | AnySubscriptionProcedure
import AnyTRPCProcedure">AnyTRPCProcedure</data-lsp></span><span style="color: #C9D1D9">&gt; </span><span style="color: #FF7B72">=</span><span style="color: #C9D1D9"> (</span></div><div class="line"><span style="color: #C9D1D9">  </span><span style="color: #FFA657"><data-lsp lsp="(parameter) input: inferProcedureInput<TProcedure>">input</data-lsp></span><span style="color: #FF7B72">:</span><span style="color: #C9D1D9"> </span><span style="color: #FFA657"><data-lsp lsp="(alias) type inferProcedureInput<TProcedure extends AnyProcedure> = undefined extends inferProcedureParams<TProcedure>[&quot;$types&quot;][&quot;input&quot;] ? void | inferProcedureParams<TProcedure>[&quot;$types&quot;][&quot;input&quot;] : inferProcedureParams<TProcedure>[&quot;$types&quot;][&quot;input&quot;]
import inferProcedureInput">inferProcedureInput</data-lsp></span><span style="color: #C9D1D9">&lt;</span><span style="color: #FFA657"><data-lsp lsp="(type parameter) TProcedure in type Resolver<TProcedure extends AnyTRPCProcedure>">TProcedure</data-lsp></span><span style="color: #C9D1D9">&gt;,</span></div><div class="line"><span style="color: #C9D1D9">) </span><span style="color: #FF7B72">=&gt;</span><span style="color: #C9D1D9"> </span><span style="color: #FFA657"><data-lsp lsp="interface Promise<T>">Promise</data-lsp></span><span style="color: #C9D1D9">&lt;</span><span style="color: #FFA657"><data-lsp lsp="(alias) type inferProcedureOutput<TProcedure> = inferProcedureParams<TProcedure>[&quot;$types&quot;][&quot;output&quot;]
import inferProcedureOutput">inferProcedureOutput</data-lsp></span><span style="color: #C9D1D9">&lt;</span><span style="color: #FFA657"><data-lsp lsp="(type parameter) TProcedure in type Resolver<TProcedure extends AnyTRPCProcedure>">TProcedure</data-lsp></span><span style="color: #C9D1D9">&gt;&gt;;</span></div><div class="line">&nbsp;</div></code></div><button type="button" aria-label="Copy code to clipboard" class="copy-button" onclick="navigator.clipboard.writeText(this.previousSibling.innerText),this.classList.add(&quot;copied&quot;),this.textContent=&quot;Copied&quot;,setTimeout(()=>{this.classList.remove(&quot;copied&quot;),this.textContent=&quot;Copy&quot;},2e3)">Copy</button></pre></div>
<p>Låt oss testa detta på vår <code>post.byId</code>-procedure:</p>
<div><pre class="shiki min-light twoslash lsp" style="background-color: #ffffff; color: #24292eff"><div class="language-id">ts</div><div class="code-container"><code><div class="line"><span style="color: #D32F2F">type</span><span style="color: #24292EFF"> </span><span style="color: #6F42C1"><data-lsp lsp="type PostById = (input: {
    id: string;
}) => Promise<Post>" style="border-bottom: solid 2px lightgrey;">PostById</data-lsp></span><span style="color: #24292EFF"> </span><span style="color: #D32F2F">=</span><span style="color: #24292EFF"> </span><span style="color: #6F42C1"><data-lsp lsp="type Resolver<TProcedure extends AnyTRPCProcedure> = (input: inferProcedureInput<TProcedure>) => Promise<inferProcedureOutput<TProcedure>>">Resolver</data-lsp></span><span style="color: #24292EFF">&lt;</span><span style="color: #6F42C1"><data-lsp lsp="type AppRouter = Router<{
    ctx: object;
    meta: object;
    errorShape: DefaultErrorShape;
    transformer: false;
}, DecorateCreateRouterOptions<{
    post: BuiltRouter<{
        ctx: object;
        meta: object;
        errorShape: DefaultErrorShape;
        transformer: false;
    }, DecorateCreateRouterOptions<{
        byId: QueryProcedure<{
            input: {
                id: string;
            };
            output: Post;
            meta: object;
        }>;
    }>>;
}>> &amp; DecorateCreateRouterOptions<{
    post: BuiltRouter<{
        ctx: object;
        meta: object;
        errorShape: DefaultErrorShape;
        transformer: false;
    }, DecorateCreateRouterOptions<{
        byId: QueryProcedure<{
            input: {
                id: string;
            };
            output: Post;
            meta: object;
        }>;
    }>>;
}>">AppRouter</data-lsp></span><span style="color: #24292EFF">[</span><span style="color: #22863A">'post'</span><span style="color: #24292EFF">][</span><span style="color: #22863A">'byId'</span><span style="color: #24292EFF">]&gt;;</span></div><div class="meta-line"><span class="popover-prefix">        </span><span class="popover"><div class="arrow"></div>type PostById = (input: {
    id: string;
}) =&gt; Promise&lt;Post&gt;</span></div></code></div><button type="button" aria-label="Copy code to clipboard" class="copy-button" onclick="navigator.clipboard.writeText(this.previousSibling.innerText),this.classList.add(&quot;copied&quot;),this.textContent=&quot;Copied&quot;,setTimeout(()=>{this.classList.remove(&quot;copied&quot;),this.textContent=&quot;Copy&quot;},2e3)">Copy</button></pre>
<pre class="shiki github-dark twoslash lsp" style="background-color: #0d1117; color: #c9d1d9"><div class="language-id">ts</div><div class="code-container"><code><div class="line"><span style="color: #FF7B72">type</span><span style="color: #C9D1D9"> </span><span style="color: #FFA657"><data-lsp lsp="type PostById = (input: {
    id: string;
}) => Promise<Post>" style="border-bottom: solid 2px lightgrey;">PostById</data-lsp></span><span style="color: #C9D1D9"> </span><span style="color: #FF7B72">=</span><span style="color: #C9D1D9"> </span><span style="color: #FFA657"><data-lsp lsp="type Resolver<TProcedure extends AnyTRPCProcedure> = (input: inferProcedureInput<TProcedure>) => Promise<inferProcedureOutput<TProcedure>>">Resolver</data-lsp></span><span style="color: #C9D1D9">&lt;</span><span style="color: #FFA657"><data-lsp lsp="type AppRouter = Router<{
    ctx: object;
    meta: object;
    errorShape: DefaultErrorShape;
    transformer: false;
}, DecorateCreateRouterOptions<{
    post: BuiltRouter<{
        ctx: object;
        meta: object;
        errorShape: DefaultErrorShape;
        transformer: false;
    }, DecorateCreateRouterOptions<{
        byId: QueryProcedure<{
            input: {
                id: string;
            };
            output: Post;
            meta: object;
        }>;
    }>>;
}>> &amp; DecorateCreateRouterOptions<{
    post: BuiltRouter<{
        ctx: object;
        meta: object;
        errorShape: DefaultErrorShape;
        transformer: false;
    }, DecorateCreateRouterOptions<{
        byId: QueryProcedure<{
            input: {
                id: string;
            };
            output: Post;
            meta: object;
        }>;
    }>>;
}>">AppRouter</data-lsp></span><span style="color: #C9D1D9">[</span><span style="color: #A5D6FF">'post'</span><span style="color: #C9D1D9">][</span><span style="color: #A5D6FF">'byId'</span><span style="color: #C9D1D9">]&gt;;</span></div><div class="meta-line"><span class="popover-prefix">        </span><span class="popover"><div class="arrow"></div>type PostById = (input: {
    id: string;
}) =&gt; Promise&lt;Post&gt;</span></div></code></div><button type="button" aria-label="Copy code to clipboard" class="copy-button" onclick="navigator.clipboard.writeText(this.previousSibling.innerText),this.classList.add(&quot;copied&quot;),this.textContent=&quot;Copied&quot;,setTimeout(()=>{this.classList.remove(&quot;copied&quot;),this.textContent=&quot;Copy&quot;},2e3)">Copy</button></pre></div>
<p>Bra, det är precis vad vi förväntade oss - vi kan nu anropa <code>.query</code> på vår procedure och få korrekta infererade input- och outputtyper!</p>
<p>Slutligen skapar vi en typ som rekursivt traverserar routern och dekorera alla procedurer längs vägen:</p>
<div></div>
<div><pre class="shiki min-light twoslash lsp" style="background-color: #ffffff; color: #24292eff"><div class="language-id">ts</div><div class="code-container"><code><div class="line"><span style="color: #D32F2F">import</span><span style="color: #24292EFF"> </span><span style="color: #D32F2F">type</span><span style="color: #24292EFF"> { <data-lsp lsp="(alias) interface TRPCRouterRecord
import TRPCRouterRecord">TRPCRouterRecord</data-lsp> } </span><span style="color: #D32F2F">from</span><span style="color: #24292EFF"> </span><span style="color: #22863A">"@trpc/server"</span><span style="color: #24292EFF">;</span></div><div class="line"><span style="color: #D32F2F">import</span><span style="color: #24292EFF"> </span><span style="color: #D32F2F">type</span><span style="color: #24292EFF"> { <data-lsp lsp="(alias) type AnyTRPCRouter = Router<any, any>
import AnyTRPCRouter">AnyTRPCRouter</data-lsp> } </span><span style="color: #D32F2F">from</span><span style="color: #24292EFF"> </span><span style="color: #22863A">"@trpc/server"</span><span style="color: #24292EFF">;</span></div><div class="line">&nbsp;</div><div class="line"><span style="color: #D32F2F">type</span><span style="color: #24292EFF"> </span><span style="color: #6F42C1"><data-lsp lsp="type DecorateRouterRecord<TRecord extends TRPCRouterRecord> = { [TKey in keyof TRecord]: TRecord[TKey] extends infer $Value ? $Value extends TRPCRouterRecord ? DecorateRouterRecord<$Value> : $Value extends AnyTRPCProcedure ? DecorateProcedure<$Value> : never : never; }">DecorateRouterRecord</data-lsp></span><span style="color: #24292EFF">&lt;</span><span style="color: #6F42C1"><data-lsp lsp="(type parameter) TRecord in type DecorateRouterRecord<TRecord extends TRPCRouterRecord>">TRecord</data-lsp></span><span style="color: #24292EFF"> </span><span style="color: #D32F2F">extends</span><span style="color: #24292EFF"> </span><span style="color: #6F42C1"><data-lsp lsp="(alias) interface TRPCRouterRecord
import TRPCRouterRecord">TRPCRouterRecord</data-lsp></span><span style="color: #24292EFF">&gt; </span><span style="color: #D32F2F">=</span><span style="color: #24292EFF"> {</span></div><div class="line"><span style="color: #24292EFF">  [</span><span style="color: #6F42C1"><data-lsp lsp="(type parameter) TKey">TKey</data-lsp></span><span style="color: #24292EFF"> </span><span style="color: #D32F2F">in</span><span style="color: #24292EFF"> </span><span style="color: #D32F2F">keyof</span><span style="color: #24292EFF"> </span><span style="color: #6F42C1"><data-lsp lsp="(type parameter) TRecord in type DecorateRouterRecord<TRecord extends TRPCRouterRecord>">TRecord</data-lsp></span><span style="color: #24292EFF">]</span><span style="color: #D32F2F">:</span><span style="color: #24292EFF"> </span><span style="color: #6F42C1"><data-lsp lsp="(type parameter) TRecord in type DecorateRouterRecord<TRecord extends TRPCRouterRecord>">TRecord</data-lsp></span><span style="color: #24292EFF">[</span><span style="color: #6F42C1"><data-lsp lsp="(type parameter) TKey">TKey</data-lsp></span><span style="color: #24292EFF">] </span><span style="color: #D32F2F">extends</span><span style="color: #24292EFF"> </span><span style="color: #D32F2F">infer</span><span style="color: #24292EFF"> </span><span style="color: #6F42C1"><data-lsp lsp="(type parameter) $Value">$Value</data-lsp></span></div><div class="line"><span style="color: #24292EFF">    </span><span style="color: #D32F2F">?</span><span style="color: #24292EFF"> </span><span style="color: #6F42C1"><data-lsp lsp="(type parameter) $Value">$Value</data-lsp></span><span style="color: #24292EFF"> </span><span style="color: #D32F2F">extends</span><span style="color: #24292EFF"> </span><span style="color: #6F42C1"><data-lsp lsp="(alias) interface TRPCRouterRecord
import TRPCRouterRecord">TRPCRouterRecord</data-lsp></span></div><div class="line"><span style="color: #24292EFF">      </span><span style="color: #D32F2F">?</span><span style="color: #24292EFF"> </span><span style="color: #6F42C1"><data-lsp lsp="type DecorateRouterRecord<TRecord extends TRPCRouterRecord> = { [TKey in keyof TRecord]: TRecord[TKey] extends infer $Value ? $Value extends TRPCRouterRecord ? DecorateRouterRecord<$Value> : $Value extends AnyTRPCProcedure ? DecorateProcedure<$Value> : never : never; }">DecorateRouterRecord</data-lsp></span><span style="color: #24292EFF">&lt;</span><span style="color: #6F42C1"><data-lsp lsp="(type parameter) $Value">$Value</data-lsp></span><span style="color: #24292EFF">&gt;</span></div><div class="line"><span style="color: #24292EFF">      </span><span style="color: #D32F2F">:</span><span style="color: #24292EFF"> </span><span style="color: #6F42C1"><data-lsp lsp="(type parameter) $Value">$Value</data-lsp></span><span style="color: #24292EFF"> </span><span style="color: #D32F2F">extends</span><span style="color: #24292EFF"> </span><span style="color: #6F42C1"><data-lsp lsp="(alias) type AnyTRPCProcedure = AnyTRPCQueryProcedure | AnyTRPCMutationProcedure | AnySubscriptionProcedure
import AnyTRPCProcedure">AnyTRPCProcedure</data-lsp></span></div><div class="line"><span style="color: #24292EFF">      </span><span style="color: #D32F2F">?</span><span style="color: #24292EFF"> </span><span style="color: #6F42C1"><data-lsp lsp="type DecorateProcedure<TProcedure> = TProcedure extends AnyTRPCQueryProcedure ? {
    query: Resolver<TProcedure>;
} : TProcedure extends AnyTRPCMutationProcedure ? {
    mutate: Resolver<TProcedure>;
} : never">DecorateProcedure</data-lsp></span><span style="color: #24292EFF">&lt;</span><span style="color: #6F42C1"><data-lsp lsp="(type parameter) $Value">$Value</data-lsp></span><span style="color: #24292EFF">&gt;</span></div><div class="line"><span style="color: #24292EFF">      </span><span style="color: #D32F2F">:</span><span style="color: #24292EFF"> </span><span style="color: #1976D2">never</span></div><div class="line"><span style="color: #24292EFF">    </span><span style="color: #D32F2F">:</span><span style="color: #24292EFF"> </span><span style="color: #1976D2">never</span><span style="color: #24292EFF">;</span></div><div class="line"><span style="color: #24292EFF">};</span></div></code></div><button type="button" aria-label="Copy code to clipboard" class="copy-button" onclick="navigator.clipboard.writeText(this.previousSibling.innerText),this.classList.add(&quot;copied&quot;),this.textContent=&quot;Copied&quot;,setTimeout(()=>{this.classList.remove(&quot;copied&quot;),this.textContent=&quot;Copy&quot;},2e3)">Copy</button></pre>
<pre class="shiki github-dark twoslash lsp" style="background-color: #0d1117; color: #c9d1d9"><div class="language-id">ts</div><div class="code-container"><code><div class="line"><span style="color: #FF7B72">import</span><span style="color: #C9D1D9"> </span><span style="color: #FF7B72">type</span><span style="color: #C9D1D9"> { <data-lsp lsp="(alias) interface TRPCRouterRecord
import TRPCRouterRecord">TRPCRouterRecord</data-lsp> } </span><span style="color: #FF7B72">from</span><span style="color: #C9D1D9"> </span><span style="color: #A5D6FF">"@trpc/server"</span><span style="color: #C9D1D9">;</span></div><div class="line"><span style="color: #FF7B72">import</span><span style="color: #C9D1D9"> </span><span style="color: #FF7B72">type</span><span style="color: #C9D1D9"> { <data-lsp lsp="(alias) type AnyTRPCRouter = Router<any, any>
import AnyTRPCRouter">AnyTRPCRouter</data-lsp> } </span><span style="color: #FF7B72">from</span><span style="color: #C9D1D9"> </span><span style="color: #A5D6FF">"@trpc/server"</span><span style="color: #C9D1D9">;</span></div><div class="line">&nbsp;</div><div class="line"><span style="color: #FF7B72">type</span><span style="color: #C9D1D9"> </span><span style="color: #FFA657"><data-lsp lsp="type DecorateRouterRecord<TRecord extends TRPCRouterRecord> = { [TKey in keyof TRecord]: TRecord[TKey] extends infer $Value ? $Value extends TRPCRouterRecord ? DecorateRouterRecord<$Value> : $Value extends AnyTRPCProcedure ? DecorateProcedure<$Value> : never : never; }">DecorateRouterRecord</data-lsp></span><span style="color: #C9D1D9">&lt;</span><span style="color: #FFA657"><data-lsp lsp="(type parameter) TRecord in type DecorateRouterRecord<TRecord extends TRPCRouterRecord>">TRecord</data-lsp></span><span style="color: #C9D1D9"> </span><span style="color: #FF7B72">extends</span><span style="color: #C9D1D9"> </span><span style="color: #FFA657"><data-lsp lsp="(alias) interface TRPCRouterRecord
import TRPCRouterRecord">TRPCRouterRecord</data-lsp></span><span style="color: #C9D1D9">&gt; </span><span style="color: #FF7B72">=</span><span style="color: #C9D1D9"> {</span></div><div class="line"><span style="color: #C9D1D9">  [</span><span style="color: #FFA657"><data-lsp lsp="(type parameter) TKey">TKey</data-lsp></span><span style="color: #C9D1D9"> </span><span style="color: #FF7B72">in</span><span style="color: #C9D1D9"> </span><span style="color: #FF7B72">keyof</span><span style="color: #C9D1D9"> </span><span style="color: #FFA657"><data-lsp lsp="(type parameter) TRecord in type DecorateRouterRecord<TRecord extends TRPCRouterRecord>">TRecord</data-lsp></span><span style="color: #C9D1D9">]</span><span style="color: #FF7B72">:</span><span style="color: #C9D1D9"> </span><span style="color: #FFA657"><data-lsp lsp="(type parameter) TRecord in type DecorateRouterRecord<TRecord extends TRPCRouterRecord>">TRecord</data-lsp></span><span style="color: #C9D1D9">[</span><span style="color: #FFA657"><data-lsp lsp="(type parameter) TKey">TKey</data-lsp></span><span style="color: #C9D1D9">] </span><span style="color: #FF7B72">extends</span><span style="color: #C9D1D9"> </span><span style="color: #FF7B72">infer</span><span style="color: #C9D1D9"> </span><span style="color: #FFA657"><data-lsp lsp="(type parameter) $Value">$Value</data-lsp></span></div><div class="line"><span style="color: #C9D1D9">    </span><span style="color: #FF7B72">?</span><span style="color: #C9D1D9"> </span><span style="color: #FFA657"><data-lsp lsp="(type parameter) $Value">$Value</data-lsp></span><span style="color: #C9D1D9"> </span><span style="color: #FF7B72">extends</span><span style="color: #C9D1D9"> </span><span style="color: #FFA657"><data-lsp lsp="(alias) interface TRPCRouterRecord
import TRPCRouterRecord">TRPCRouterRecord</data-lsp></span></div><div class="line"><span style="color: #C9D1D9">      </span><span style="color: #FF7B72">?</span><span style="color: #C9D1D9"> </span><span style="color: #FFA657"><data-lsp lsp="type DecorateRouterRecord<TRecord extends TRPCRouterRecord> = { [TKey in keyof TRecord]: TRecord[TKey] extends infer $Value ? $Value extends TRPCRouterRecord ? DecorateRouterRecord<$Value> : $Value extends AnyTRPCProcedure ? DecorateProcedure<$Value> : never : never; }">DecorateRouterRecord</data-lsp></span><span style="color: #C9D1D9">&lt;</span><span style="color: #FFA657"><data-lsp lsp="(type parameter) $Value">$Value</data-lsp></span><span style="color: #C9D1D9">&gt;</span></div><div class="line"><span style="color: #C9D1D9">      </span><span style="color: #FF7B72">:</span><span style="color: #C9D1D9"> </span><span style="color: #FFA657"><data-lsp lsp="(type parameter) $Value">$Value</data-lsp></span><span style="color: #C9D1D9"> </span><span style="color: #FF7B72">extends</span><span style="color: #C9D1D9"> </span><span style="color: #FFA657"><data-lsp lsp="(alias) type AnyTRPCProcedure = AnyTRPCQueryProcedure | AnyTRPCMutationProcedure | AnySubscriptionProcedure
import AnyTRPCProcedure">AnyTRPCProcedure</data-lsp></span></div><div class="line"><span style="color: #C9D1D9">      </span><span style="color: #FF7B72">?</span><span style="color: #C9D1D9"> </span><span style="color: #FFA657"><data-lsp lsp="type DecorateProcedure<TProcedure> = TProcedure extends AnyTRPCQueryProcedure ? {
    query: Resolver<TProcedure>;
} : TProcedure extends AnyTRPCMutationProcedure ? {
    mutate: Resolver<TProcedure>;
} : never">DecorateProcedure</data-lsp></span><span style="color: #C9D1D9">&lt;</span><span style="color: #FFA657"><data-lsp lsp="(type parameter) $Value">$Value</data-lsp></span><span style="color: #C9D1D9">&gt;</span></div><div class="line"><span style="color: #C9D1D9">      </span><span style="color: #FF7B72">:</span><span style="color: #C9D1D9"> </span><span style="color: #79C0FF">never</span></div><div class="line"><span style="color: #C9D1D9">    </span><span style="color: #FF7B72">:</span><span style="color: #C9D1D9"> </span><span style="color: #79C0FF">never</span><span style="color: #C9D1D9">;</span></div><div class="line"><span style="color: #C9D1D9">};</span></div></code></div><button type="button" aria-label="Copy code to clipboard" class="copy-button" onclick="navigator.clipboard.writeText(this.previousSibling.innerText),this.classList.add(&quot;copied&quot;),this.textContent=&quot;Copied&quot;,setTimeout(()=>{this.classList.remove(&quot;copied&quot;),this.textContent=&quot;Copy&quot;},2e3)">Copy</button></pre></div>
<p>Låt oss bryta ned denna typ lite:</p>
<ol>
<li>
<p>Vi skickar in en <code>TRPCRouterRecord</code> till typen som en generic, vilket är en typ som innehåller alla procedurer och underrouter som finns på en tRPC-router.</p>
</li>
<li>
<p>Vi itererar över nycklarna i record-objektet, som är procedur- eller router-namn, och gör följande:</p>
<ul>
<li>Om nyckeln pekar på en router, anropar vi typen rekursivt på den routerns procedur-record, vilket kommer att dekorera alla procedurer i den routern. Detta ger autofyllning när vi navigerar genom sökvägen.</li>
<li>Om nyckeln pekar på en procedur, dekorerar vi proceduren med <code>DecorateProcedure</code>-typen vi skapade tidigare.</li>
<li>Om nyckeln inte pekar på en procedur eller router, tilldelar vi <code>never</code>-typen vilket i praktiken betyder "denna nyckel existerar inte" och ger ett typfel om vi försöker komma åt den.</li>
</ul>
</li>
</ol>
<h3 class="anchor anchorWithStickyNavbar_MYIC" id="-proxy-omvandlingen">🤯 Proxy-omvandlingen<a href="https://trpc.io/sv/blog/tinyrpc-client#-proxy-omvandlingen" class="hash-link" aria-label="Direktlänk till 🤯 Proxy-omvandlingen" title="Direktlänk till 🤯 Proxy-omvandlingen">​</a></h3>
<p>Nu när vi har alla typer på plats måste vi implementera funktionaliteten som omvandlar serverns router-definition på klienten så att vi kan anropa procedurer som vanliga funktioner.</p>
<p>Vi börjar med att skapa en hjälpfunktion för rekursiva proxies - <code>createRecursiveProxy</code>:</p>
<div class="theme-admonition theme-admonition-info admonition_v5Ag alert alert--info"><div class="admonitionHeading_usrK"><span class="admonitionIcon_bgEp"><svg viewBox="0 0 14 16"><path fill-rule="evenodd" d="M7 2.3c3.14 0 5.7 2.56 5.7 5.7s-2.56 5.7-5.7 5.7A5.71 5.71 0 0 1 1.3 8c0-3.14 2.56-5.7 5.7-5.7zM7 1C3.14 1 0 4.14 0 8s3.14 7 7 7 7-3.14 7-7-3.14-7-7-7zm1 3H6v5h2V4zm0 6H6v2h2v-2z"></path></svg></span>information</div><div class="admonitionContent_e2NW"><p>Detta är nästan exakt samma implementation som används i produktion, med undantag för att vi inte hanterar vissa edge cases. <a href="https://github.com/trpc/trpc/blob/main/packages/server/src/shared/createProxy/index.ts" target="_blank" rel="noopener noreferrer">Se själv</a>!</p></div></div>
<div></div>
<div><pre class="shiki min-light twoslash lsp" style="background-color: #ffffff; color: #24292eff"><div class="language-id">ts</div><div class="code-container"><code><div class="line"><span style="color: #D32F2F">interface</span><span style="color: #24292EFF"> </span><span style="color: #6F42C1"><data-lsp lsp="interface ProxyCallbackOptions">ProxyCallbackOptions</data-lsp></span><span style="color: #24292EFF"> {</span></div><div class="line"><span style="color: #24292EFF">  <data-lsp lsp="(property) ProxyCallbackOptions.path: readonly string[]">path</data-lsp></span><span style="color: #D32F2F">:</span><span style="color: #24292EFF"> </span><span style="color: #D32F2F">readonly</span><span style="color: #24292EFF"> </span><span style="color: #1976D2">string</span><span style="color: #24292EFF">[];</span></div><div class="line"><span style="color: #24292EFF">  <data-lsp lsp="(property) ProxyCallbackOptions.args: readonly unknown[]">args</data-lsp></span><span style="color: #D32F2F">:</span><span style="color: #24292EFF"> </span><span style="color: #D32F2F">readonly</span><span style="color: #24292EFF"> </span><span style="color: #1976D2">unknown</span><span style="color: #24292EFF">[];</span></div><div class="line"><span style="color: #24292EFF">}</span></div><div class="line">&nbsp;</div><div class="line"><span style="color: #D32F2F">type</span><span style="color: #24292EFF"> </span><span style="color: #6F42C1"><data-lsp lsp="type ProxyCallback = (opts: ProxyCallbackOptions) => unknown">ProxyCallback</data-lsp></span><span style="color: #24292EFF"> </span><span style="color: #D32F2F">=</span><span style="color: #24292EFF"> (<data-lsp lsp="(parameter) opts: ProxyCallbackOptions">opts</data-lsp></span><span style="color: #D32F2F">:</span><span style="color: #24292EFF"> </span><span style="color: #6F42C1"><data-lsp lsp="interface ProxyCallbackOptions">ProxyCallbackOptions</data-lsp></span><span style="color: #24292EFF">) </span><span style="color: #D32F2F">=&gt;</span><span style="color: #24292EFF"> </span><span style="color: #1976D2">unknown</span><span style="color: #24292EFF">;</span></div><div class="line">&nbsp;</div><div class="line"><span style="color: #D32F2F">function</span><span style="color: #24292EFF"> </span><span style="color: #6F42C1"><data-lsp lsp="function createRecursiveProxy(callback: ProxyCallback, path: readonly string[]): unknown">createRecursiveProxy</data-lsp></span><span style="color: #24292EFF">(<data-lsp lsp="(parameter) callback: ProxyCallback">callback</data-lsp></span><span style="color: #D32F2F">:</span><span style="color: #24292EFF"> </span><span style="color: #6F42C1"><data-lsp lsp="type ProxyCallback = (opts: ProxyCallbackOptions) => unknown">ProxyCallback</data-lsp></span><span style="color: #212121">,</span><span style="color: #24292EFF"> <data-lsp lsp="(parameter) path: readonly string[]">path</data-lsp></span><span style="color: #D32F2F">:</span><span style="color: #24292EFF"> </span><span style="color: #D32F2F">readonly</span><span style="color: #24292EFF"> </span><span style="color: #1976D2">string</span><span style="color: #24292EFF">[]) {</span></div><div class="line"><span style="color: #24292EFF">  </span><span style="color: #D32F2F">const</span><span style="color: #24292EFF"> </span><span style="color: #1976D2"><data-lsp lsp="const proxy: unknown">proxy</data-lsp></span><span style="color: #D32F2F">:</span><span style="color: #24292EFF"> </span><span style="color: #1976D2">unknown</span><span style="color: #24292EFF"> </span><span style="color: #D32F2F">=</span><span style="color: #24292EFF"> </span><span style="color: #D32F2F">new</span><span style="color: #24292EFF"> </span><span style="color: #1976D2"><data-lsp lsp="var Proxy: ProxyConstructor
new <() => void>(target: () => void, handler: ProxyHandler<() => void>) => () => void">Proxy</data-lsp></span><span style="color: #24292EFF">(</span></div><div class="line"><span style="color: #24292EFF">    () </span><span style="color: #D32F2F">=&gt;</span><span style="color: #24292EFF"> {</span></div><div class="line"><span style="color: #24292EFF">      </span><span style="color: #C2C3C5">// dummy no-op function since we don't have any</span></div><div class="line"><span style="color: #24292EFF">      </span><span style="color: #C2C3C5">// client-side target we want to remap to</span></div><div class="line"><span style="color: #24292EFF">    }</span><span style="color: #212121">,</span></div><div class="line"><span style="color: #24292EFF">    {</span></div><div class="line"><span style="color: #24292EFF">      </span><span style="color: #6F42C1"><data-lsp lsp="(method) ProxyHandler<() => void>.get?(target: () => void, p: string | symbol, receiver: any): any">get</data-lsp></span><span style="color: #24292EFF">(<data-lsp lsp="(parameter) _obj: () => void">_obj</data-lsp></span><span style="color: #212121">,</span><span style="color: #24292EFF"> <data-lsp lsp="(parameter) key: string | symbol">key</data-lsp>) {</span></div><div class="line"><span style="color: #24292EFF">        </span><span style="color: #D32F2F">if</span><span style="color: #24292EFF"> (</span><span style="color: #D32F2F">typeof</span><span style="color: #24292EFF"> <data-lsp lsp="(parameter) key: string | symbol">key</data-lsp> </span><span style="color: #D32F2F">!==</span><span style="color: #24292EFF"> </span><span style="color: #22863A">'string'</span><span style="color: #24292EFF">) </span><span style="color: #D32F2F">return</span><span style="color: #24292EFF"> </span><span style="color: #1976D2"><data-lsp lsp="var undefined">undefined</data-lsp></span><span style="color: #24292EFF">;</span></div><div class="line">&nbsp;</div><div class="line"><span style="color: #24292EFF">        </span><span style="color: #C2C3C5">// Recursively compose the full path until a function is invoked</span></div><div class="line"><span style="color: #24292EFF">        </span><span style="color: #D32F2F">return</span><span style="color: #24292EFF"> </span><span style="color: #6F42C1"><data-lsp lsp="function createRecursiveProxy(callback: ProxyCallback, path: readonly string[]): unknown">createRecursiveProxy</data-lsp></span><span style="color: #24292EFF">(<data-lsp lsp="(parameter) callback: ProxyCallback">callback</data-lsp></span><span style="color: #212121">,</span><span style="color: #24292EFF"> [</span><span style="color: #D32F2F">...</span><span style="color: #24292EFF"><data-lsp lsp="(parameter) path: readonly string[]">path</data-lsp></span><span style="color: #212121">,</span><span style="color: #24292EFF"> <data-lsp lsp="(parameter) key: string">key</data-lsp>]);</span></div><div class="line"><span style="color: #24292EFF">      }</span><span style="color: #212121">,</span></div><div class="line"><span style="color: #24292EFF">      </span><span style="color: #6F42C1"><data-lsp lsp="(method) ProxyHandler<() => void>.apply?(target: () => void, thisArg: any, argArray: any[]): any">apply</data-lsp></span><span style="color: #24292EFF">(<data-lsp lsp="(parameter) _1: () => void">_1</data-lsp></span><span style="color: #212121">,</span><span style="color: #24292EFF"> <data-lsp lsp="(parameter) _2: any">_2</data-lsp></span><span style="color: #212121">,</span><span style="color: #24292EFF"> <data-lsp lsp="(parameter) args: any[]">args</data-lsp>) {</span></div><div class="line"><span style="color: #24292EFF">        </span><span style="color: #C2C3C5">// Call the callback function with the entire path we</span></div><div class="line"><span style="color: #24292EFF">        </span><span style="color: #C2C3C5">// recursively created and forward the arguments</span></div><div class="line"><span style="color: #24292EFF">        </span><span style="color: #D32F2F">return</span><span style="color: #24292EFF"> </span><span style="color: #6F42C1"><data-lsp lsp="(parameter) callback: (opts: ProxyCallbackOptions) => unknown">callback</data-lsp></span><span style="color: #24292EFF">({</span></div><div class="line"><span style="color: #24292EFF">          <data-lsp lsp="(property) ProxyCallbackOptions.path: readonly string[]">path</data-lsp></span><span style="color: #212121">,</span></div><div class="line"><span style="color: #24292EFF">          <data-lsp lsp="(property) ProxyCallbackOptions.args: readonly unknown[]">args</data-lsp></span><span style="color: #212121">,</span></div><div class="line"><span style="color: #24292EFF">        });</span></div><div class="line"><span style="color: #24292EFF">      }</span><span style="color: #212121">,</span></div><div class="line"><span style="color: #24292EFF">    }</span><span style="color: #212121">,</span></div><div class="line"><span style="color: #24292EFF">  );</span></div><div class="line">&nbsp;</div><div class="line"><span style="color: #24292EFF">  </span><span style="color: #D32F2F">return</span><span style="color: #24292EFF"> <data-lsp lsp="const proxy: unknown">proxy</data-lsp>;</span></div><div class="line"><span style="color: #24292EFF">}</span></div></code></div><button type="button" aria-label="Copy code to clipboard" class="copy-button" onclick="navigator.clipboard.writeText(this.previousSibling.innerText),this.classList.add(&quot;copied&quot;),this.textContent=&quot;Copied&quot;,setTimeout(()=>{this.classList.remove(&quot;copied&quot;),this.textContent=&quot;Copy&quot;},2e3)">Copy</button></pre>
<pre class="shiki github-dark twoslash lsp" style="background-color: #0d1117; color: #c9d1d9"><div class="language-id">ts</div><div class="code-container"><code><div class="line"><span style="color: #FF7B72">interface</span><span style="color: #C9D1D9"> </span><span style="color: #FFA657"><data-lsp lsp="interface ProxyCallbackOptions">ProxyCallbackOptions</data-lsp></span><span style="color: #C9D1D9"> {</span></div><div class="line"><span style="color: #C9D1D9">  </span><span style="color: #FFA657"><data-lsp lsp="(property) ProxyCallbackOptions.path: readonly string[]">path</data-lsp></span><span style="color: #FF7B72">:</span><span style="color: #C9D1D9"> </span><span style="color: #FF7B72">readonly</span><span style="color: #C9D1D9"> </span><span style="color: #79C0FF">string</span><span style="color: #C9D1D9">[];</span></div><div class="line"><span style="color: #C9D1D9">  </span><span style="color: #FFA657"><data-lsp lsp="(property) ProxyCallbackOptions.args: readonly unknown[]">args</data-lsp></span><span style="color: #FF7B72">:</span><span style="color: #C9D1D9"> </span><span style="color: #FF7B72">readonly</span><span style="color: #C9D1D9"> </span><span style="color: #79C0FF">unknown</span><span style="color: #C9D1D9">[];</span></div><div class="line"><span style="color: #C9D1D9">}</span></div><div class="line">&nbsp;</div><div class="line"><span style="color: #FF7B72">type</span><span style="color: #C9D1D9"> </span><span style="color: #FFA657"><data-lsp lsp="type ProxyCallback = (opts: ProxyCallbackOptions) => unknown">ProxyCallback</data-lsp></span><span style="color: #C9D1D9"> </span><span style="color: #FF7B72">=</span><span style="color: #C9D1D9"> (</span><span style="color: #FFA657"><data-lsp lsp="(parameter) opts: ProxyCallbackOptions">opts</data-lsp></span><span style="color: #FF7B72">:</span><span style="color: #C9D1D9"> </span><span style="color: #FFA657"><data-lsp lsp="interface ProxyCallbackOptions">ProxyCallbackOptions</data-lsp></span><span style="color: #C9D1D9">) </span><span style="color: #FF7B72">=&gt;</span><span style="color: #C9D1D9"> </span><span style="color: #79C0FF">unknown</span><span style="color: #C9D1D9">;</span></div><div class="line">&nbsp;</div><div class="line"><span style="color: #FF7B72">function</span><span style="color: #C9D1D9"> </span><span style="color: #D2A8FF"><data-lsp lsp="function createRecursiveProxy(callback: ProxyCallback, path: readonly string[]): unknown">createRecursiveProxy</data-lsp></span><span style="color: #C9D1D9">(</span><span style="color: #FFA657"><data-lsp lsp="(parameter) callback: ProxyCallback">callback</data-lsp></span><span style="color: #FF7B72">:</span><span style="color: #C9D1D9"> </span><span style="color: #FFA657"><data-lsp lsp="type ProxyCallback = (opts: ProxyCallbackOptions) => unknown">ProxyCallback</data-lsp></span><span style="color: #C9D1D9">, </span><span style="color: #FFA657"><data-lsp lsp="(parameter) path: readonly string[]">path</data-lsp></span><span style="color: #FF7B72">:</span><span style="color: #C9D1D9"> </span><span style="color: #FF7B72">readonly</span><span style="color: #C9D1D9"> </span><span style="color: #79C0FF">string</span><span style="color: #C9D1D9">[]) {</span></div><div class="line"><span style="color: #C9D1D9">  </span><span style="color: #FF7B72">const</span><span style="color: #C9D1D9"> </span><span style="color: #79C0FF"><data-lsp lsp="const proxy: unknown">proxy</data-lsp></span><span style="color: #FF7B72">:</span><span style="color: #C9D1D9"> </span><span style="color: #79C0FF">unknown</span><span style="color: #C9D1D9"> </span><span style="color: #FF7B72">=</span><span style="color: #C9D1D9"> </span><span style="color: #FF7B72">new</span><span style="color: #C9D1D9"> </span><span style="color: #79C0FF"><data-lsp lsp="var Proxy: ProxyConstructor
new <() => void>(target: () => void, handler: ProxyHandler<() => void>) => () => void">Proxy</data-lsp></span><span style="color: #C9D1D9">(</span></div><div class="line"><span style="color: #C9D1D9">    () </span><span style="color: #FF7B72">=&gt;</span><span style="color: #C9D1D9"> {</span></div><div class="line"><span style="color: #C9D1D9">      </span><span style="color: #8B949E">// dummy no-op function since we don't have any</span></div><div class="line"><span style="color: #C9D1D9">      </span><span style="color: #8B949E">// client-side target we want to remap to</span></div><div class="line"><span style="color: #C9D1D9">    },</span></div><div class="line"><span style="color: #C9D1D9">    {</span></div><div class="line"><span style="color: #C9D1D9">      </span><span style="color: #D2A8FF"><data-lsp lsp="(method) ProxyHandler<() => void>.get?(target: () => void, p: string | symbol, receiver: any): any">get</data-lsp></span><span style="color: #C9D1D9">(</span><span style="color: #FFA657"><data-lsp lsp="(parameter) _obj: () => void">_obj</data-lsp></span><span style="color: #C9D1D9">, </span><span style="color: #FFA657"><data-lsp lsp="(parameter) key: string | symbol">key</data-lsp></span><span style="color: #C9D1D9">) {</span></div><div class="line"><span style="color: #C9D1D9">        </span><span style="color: #FF7B72">if</span><span style="color: #C9D1D9"> (</span><span style="color: #FF7B72">typeof</span><span style="color: #C9D1D9"> <data-lsp lsp="(parameter) key: string | symbol">key</data-lsp> </span><span style="color: #FF7B72">!==</span><span style="color: #C9D1D9"> </span><span style="color: #A5D6FF">'string'</span><span style="color: #C9D1D9">) </span><span style="color: #FF7B72">return</span><span style="color: #C9D1D9"> </span><span style="color: #79C0FF"><data-lsp lsp="var undefined">undefined</data-lsp></span><span style="color: #C9D1D9">;</span></div><div class="line">&nbsp;</div><div class="line"><span style="color: #C9D1D9">        </span><span style="color: #8B949E">// Recursively compose the full path until a function is invoked</span></div><div class="line"><span style="color: #C9D1D9">        </span><span style="color: #FF7B72">return</span><span style="color: #C9D1D9"> </span><span style="color: #D2A8FF"><data-lsp lsp="function createRecursiveProxy(callback: ProxyCallback, path: readonly string[]): unknown">createRecursiveProxy</data-lsp></span><span style="color: #C9D1D9">(<data-lsp lsp="(parameter) callback: ProxyCallback">callback</data-lsp>, [</span><span style="color: #FF7B72">...</span><span style="color: #C9D1D9"><data-lsp lsp="(parameter) path: readonly string[]">path</data-lsp>, <data-lsp lsp="(parameter) key: string">key</data-lsp>]);</span></div><div class="line"><span style="color: #C9D1D9">      },</span></div><div class="line"><span style="color: #C9D1D9">      </span><span style="color: #D2A8FF"><data-lsp lsp="(method) ProxyHandler<() => void>.apply?(target: () => void, thisArg: any, argArray: any[]): any">apply</data-lsp></span><span style="color: #C9D1D9">(</span><span style="color: #FFA657"><data-lsp lsp="(parameter) _1: () => void">_1</data-lsp></span><span style="color: #C9D1D9">, </span><span style="color: #FFA657"><data-lsp lsp="(parameter) _2: any">_2</data-lsp></span><span style="color: #C9D1D9">, </span><span style="color: #FFA657"><data-lsp lsp="(parameter) args: any[]">args</data-lsp></span><span style="color: #C9D1D9">) {</span></div><div class="line"><span style="color: #C9D1D9">        </span><span style="color: #8B949E">// Call the callback function with the entire path we</span></div><div class="line"><span style="color: #C9D1D9">        </span><span style="color: #8B949E">// recursively created and forward the arguments</span></div><div class="line"><span style="color: #C9D1D9">        </span><span style="color: #FF7B72">return</span><span style="color: #C9D1D9"> </span><span style="color: #D2A8FF"><data-lsp lsp="(parameter) callback: (opts: ProxyCallbackOptions) => unknown">callback</data-lsp></span><span style="color: #C9D1D9">({</span></div><div class="line"><span style="color: #C9D1D9">          <data-lsp lsp="(property) ProxyCallbackOptions.path: readonly string[]">path</data-lsp>,</span></div><div class="line"><span style="color: #C9D1D9">          <data-lsp lsp="(property) ProxyCallbackOptions.args: readonly unknown[]">args</data-lsp>,</span></div><div class="line"><span style="color: #C9D1D9">        });</span></div><div class="line"><span style="color: #C9D1D9">      },</span></div><div class="line"><span style="color: #C9D1D9">    },</span></div><div class="line"><span style="color: #C9D1D9">  );</span></div><div class="line">&nbsp;</div><div class="line"><span style="color: #C9D1D9">  </span><span style="color: #FF7B72">return</span><span style="color: #C9D1D9"> <data-lsp lsp="const proxy: unknown">proxy</data-lsp>;</span></div><div class="line"><span style="color: #C9D1D9">}</span></div></code></div><button type="button" aria-label="Copy code to clipboard" class="copy-button" onclick="navigator.clipboard.writeText(this.previousSibling.innerText),this.classList.add(&quot;copied&quot;),this.textContent=&quot;Copied&quot;,setTimeout(()=>{this.classList.remove(&quot;copied&quot;),this.textContent=&quot;Copy&quot;},2e3)">Copy</button></pre></div>
<p>Det här ser lite magiskt ut, vad gör den egentligen?</p>
<ul>
<li>
<p><code>get</code>-metoden hanterar egenskapsåtkomst som t.ex. <code>post.byId</code>. Nyckeln är egenskapens namn vi kommer åt, så när vi skriver <code>post</code> blir vår <code>key</code> <code>post</code>, och när vi skriver <code>post.byId</code> blir vår <code>key</code> <code>byId</code>. Den rekursiva proxyn kombinerar alla dessa nycklar till en slutlig sökväg, t.ex. ["post", "byId", "query"], som vi kan använda för att bestämma vilken URL vi ska skicka vår förfrågan till.</p>
</li>
<li>
<p><code>apply</code>-metoden anropas när vi exekverar en funktion på proxyn, som t.ex. <code>.query(args)</code>. <code>args</code> är argumenten vi skickar till funktionen, så när vi anropar <code>post.byId.query(args)</code> kommer vårt <code>args</code> att vara vårt input som vi skickar som query-parametrar eller request body beroende på procedurtyp. <code>createRecursiveProxy</code> tar emot en callback-funktion som vi mappar <code>apply</code> till med sökvägen och argumenten.</p>
</li>
</ul>
<p>Här är en visuell representation av hur proxyn fungerar vid anropet <code>trpc.post.byId.query({ id: 1 })</code>:</p>
<p><img decoding="async" loading="lazy" src="https://assets.trpc.io/www/blog/2023-01-17-tinyrpc-client/proxy.png" alt="proxy" class="img_Njog"></p>
<h3 class="anchor anchorWithStickyNavbar_MYIC" id="-sätt-ihop-allt">🧩 Sätt ihop allt<a href="https://trpc.io/sv/blog/tinyrpc-client#-s%C3%A4tt-ihop-allt" class="hash-link" aria-label="Direktlänk till 🧩 Sätt ihop allt" title="Direktlänk till 🧩 Sätt ihop allt">​</a></h3>
<p>Nu när vi har denna hjälpfunktion och vet vad den gör, låt oss använda den för att skapa vår klient. Vi ger <code>createRecursiveProxy</code> en callback som tar emot sökvägen och argumenten och skickar en förfrågan till servern med <code>fetch</code>. Vi lägger till en generisk typ på funktionen som accepterar vilken tRPC-router som helst (<code>AnyTRPCRouter</code>), och sedan castar vi returtypen till <code>DecorateRouterRecord</code>-typen vi skapade tidigare:</p>
<div></div>
<div><pre class="shiki min-light twoslash lsp" style="background-color: #ffffff; color: #24292eff"><div class="language-id">ts</div><div class="code-container"><code><div class="line"><span style="color: #D32F2F">import</span><span style="color: #24292EFF"> { <data-lsp lsp="(alias) type TRPCResponse<TData = unknown, TError extends TRPCErrorShape = TRPCErrorShape<object>> = TRPCErrorResponse<TError> | TRPCSuccessResponse<TData>
import TRPCResponse">TRPCResponse</data-lsp> } </span><span style="color: #D32F2F">from</span><span style="color: #24292EFF"> </span><span style="color: #22863A">'@trpc/server/rpc'</span><span style="color: #24292EFF">;</span></div><div class="line">&nbsp;</div><div class="line"><span style="color: #D32F2F">export</span><span style="color: #24292EFF"> </span><span style="color: #D32F2F">const</span><span style="color: #24292EFF"> </span><span style="color: #6F42C1"><data-lsp lsp="const createTinyRPCClient: <TRouter extends AnyTRPCRouter>(baseUrl: string) => DecorateRouterRecord<TRouter[&quot;_def&quot;][&quot;record&quot;]>">createTinyRPCClient</data-lsp></span><span style="color: #24292EFF"> </span><span style="color: #D32F2F">=</span><span style="color: #24292EFF"> &lt;</span><span style="color: #6F42C1"><data-lsp lsp="(type parameter) TRouter in <TRouter extends AnyTRPCRouter>(baseUrl: string): DecorateRouterRecord<TRouter[&quot;_def&quot;][&quot;record&quot;]>">TRouter</data-lsp></span><span style="color: #24292EFF"> </span><span style="color: #D32F2F">extends</span><span style="color: #24292EFF"> </span><span style="color: #6F42C1"><data-lsp lsp="(alias) type AnyTRPCRouter = Router<any, any>
import AnyTRPCRouter">AnyTRPCRouter</data-lsp></span><span style="color: #24292EFF">&gt;(</span></div><div class="line"><span style="color: #24292EFF">  <data-lsp lsp="(parameter) baseUrl: string">baseUrl</data-lsp></span><span style="color: #D32F2F">:</span><span style="color: #24292EFF"> </span><span style="color: #1976D2">string</span><span style="color: #212121">,</span></div><div class="line"><span style="color: #24292EFF">) </span><span style="color: #D32F2F">=&gt;</span></div><div class="line"><span style="color: #24292EFF">  </span><span style="color: #6F42C1"><data-lsp lsp="function createRecursiveProxy(callback: ProxyCallback, path: readonly string[]): unknown">createRecursiveProxy</data-lsp></span><span style="color: #24292EFF">(</span><span style="color: #D32F2F">async</span><span style="color: #24292EFF"> (<data-lsp lsp="(parameter) opts: ProxyCallbackOptions">opts</data-lsp>) </span><span style="color: #D32F2F">=&gt;</span><span style="color: #24292EFF"> {</span></div><div class="line"><span style="color: #24292EFF">    </span><span style="color: #D32F2F">const</span><span style="color: #24292EFF"> </span><span style="color: #1976D2"><data-lsp lsp="const path: string[]">path</data-lsp></span><span style="color: #24292EFF"> </span><span style="color: #D32F2F">=</span><span style="color: #24292EFF"> [</span><span style="color: #D32F2F">...</span><span style="color: #1976D2"><data-lsp lsp="(parameter) opts: ProxyCallbackOptions">opts</data-lsp></span><span style="color: #24292EFF">.<data-lsp lsp="(property) ProxyCallbackOptions.path: readonly string[]">path</data-lsp>]; </span><span style="color: #C2C3C5">// e.g. ["post", "byId", "query"]</span></div><div class="line"><span style="color: #24292EFF">    </span><span style="color: #D32F2F">const</span><span style="color: #24292EFF"> </span><span style="color: #1976D2"><data-lsp lsp="const method: &quot;query&quot; | &quot;mutate&quot;">method</data-lsp></span><span style="color: #24292EFF"> </span><span style="color: #D32F2F">=</span><span style="color: #24292EFF"> </span><span style="color: #1976D2"><data-lsp lsp="const path: string[]">path</data-lsp></span><span style="color: #6F42C1">.<data-lsp lsp="(method) Array<string>.pop(): string | undefined">pop</data-lsp></span><span style="color: #24292EFF">()</span><span style="color: #D32F2F">!</span><span style="color: #24292EFF"> </span><span style="color: #D32F2F">as</span><span style="color: #24292EFF"> </span><span style="color: #22863A">'query'</span><span style="color: #24292EFF"> </span><span style="color: #D32F2F">|</span><span style="color: #24292EFF"> </span><span style="color: #22863A">'mutate'</span><span style="color: #24292EFF">;</span></div><div class="line"><span style="color: #24292EFF">    </span><span style="color: #D32F2F">const</span><span style="color: #24292EFF"> </span><span style="color: #1976D2"><data-lsp lsp="const dotPath: string">dotPath</data-lsp></span><span style="color: #24292EFF"> </span><span style="color: #D32F2F">=</span><span style="color: #24292EFF"> </span><span style="color: #1976D2"><data-lsp lsp="const path: string[]">path</data-lsp></span><span style="color: #6F42C1">.<data-lsp lsp="(method) Array<string>.join(separator?: string): string">join</data-lsp></span><span style="color: #24292EFF">(</span><span style="color: #22863A">'.'</span><span style="color: #24292EFF">); </span><span style="color: #C2C3C5">// "post.byId" - this is the path procedures have on the backend</span></div><div class="line"><span style="color: #24292EFF">    </span><span style="color: #D32F2F">let</span><span style="color: #24292EFF"> <data-lsp lsp="let uri: string">uri</data-lsp> </span><span style="color: #D32F2F">=</span><span style="color: #24292EFF"> </span><span style="color: #22863A">`</span><span style="color: #D32F2F">${</span><span style="color: #24292EFF"><data-lsp lsp="(parameter) baseUrl: string">baseUrl</data-lsp></span><span style="color: #D32F2F">}</span><span style="color: #22863A">/</span><span style="color: #D32F2F">${</span><span style="color: #24292EFF"><data-lsp lsp="const dotPath: string">dotPath</data-lsp></span><span style="color: #D32F2F">}</span><span style="color: #22863A">`</span><span style="color: #24292EFF">;</span></div><div class="line">&nbsp;</div><div class="line"><span style="color: #24292EFF">    </span><span style="color: #D32F2F">const</span><span style="color: #24292EFF"> [</span><span style="color: #1976D2"><data-lsp lsp="const input: unknown">input</data-lsp></span><span style="color: #24292EFF">] </span><span style="color: #D32F2F">=</span><span style="color: #24292EFF"> </span><span style="color: #1976D2"><data-lsp lsp="(parameter) opts: ProxyCallbackOptions">opts</data-lsp></span><span style="color: #24292EFF">.<data-lsp lsp="(property) ProxyCallbackOptions.args: readonly unknown[]">args</data-lsp>;</span></div><div class="line"><span style="color: #24292EFF">    </span><span style="color: #D32F2F">const</span><span style="color: #24292EFF"> </span><span style="color: #1976D2"><data-lsp lsp="const stringifiedInput: string | false">stringifiedInput</data-lsp></span><span style="color: #24292EFF"> </span><span style="color: #D32F2F">=</span><span style="color: #24292EFF"> <data-lsp lsp="const input: unknown">input</data-lsp> </span><span style="color: #D32F2F">!==</span><span style="color: #24292EFF"> </span><span style="color: #1976D2"><data-lsp lsp="var undefined">undefined</data-lsp></span><span style="color: #24292EFF"> </span><span style="color: #D32F2F">&amp;&amp;</span><span style="color: #24292EFF"> </span><span style="color: #1976D2"><data-lsp lsp="var JSON: JSON">JSON</data-lsp></span><span style="color: #6F42C1">.<data-lsp lsp="(method) JSON.stringify(value: any, replacer?: (this: any, key: string, value: any) => any, space?: string | number): string (+1 overload)">stringify</data-lsp></span><span style="color: #24292EFF">(<data-lsp lsp="const input: {} | null">input</data-lsp>);</span></div><div class="line"><span style="color: #24292EFF">    </span><span style="color: #D32F2F">let</span><span style="color: #24292EFF"> <data-lsp lsp="let body: string | undefined">body</data-lsp></span><span style="color: #D32F2F">:</span><span style="color: #24292EFF"> </span><span style="color: #1976D2">undefined</span><span style="color: #24292EFF"> </span><span style="color: #D32F2F">|</span><span style="color: #24292EFF"> </span><span style="color: #1976D2">string</span><span style="color: #24292EFF"> </span><span style="color: #D32F2F">=</span><span style="color: #24292EFF"> </span><span style="color: #1976D2"><data-lsp lsp="var undefined">undefined</data-lsp></span><span style="color: #24292EFF">;</span></div><div class="line"><span style="color: #24292EFF">    </span><span style="color: #D32F2F">if</span><span style="color: #24292EFF"> (<data-lsp lsp="const stringifiedInput: string | false">stringifiedInput</data-lsp> </span><span style="color: #D32F2F">!==</span><span style="color: #24292EFF"> </span><span style="color: #1976D2">false</span><span style="color: #24292EFF">) {</span></div><div class="line"><span style="color: #24292EFF">      </span><span style="color: #D32F2F">if</span><span style="color: #24292EFF"> (<data-lsp lsp="const method: &quot;query&quot; | &quot;mutate&quot;">method</data-lsp> </span><span style="color: #D32F2F">===</span><span style="color: #24292EFF"> </span><span style="color: #22863A">'query'</span><span style="color: #24292EFF">) {</span></div><div class="line"><span style="color: #24292EFF">        <data-lsp lsp="let uri: string">uri</data-lsp> </span><span style="color: #D32F2F">+=</span><span style="color: #24292EFF"> </span><span style="color: #22863A">`?input=</span><span style="color: #D32F2F">${</span><span style="color: #6F42C1"><data-lsp lsp="function encodeURIComponent(uriComponent: string | number | boolean): string">encodeURIComponent</data-lsp></span><span style="color: #24292EFF">(<data-lsp lsp="const stringifiedInput: string">stringifiedInput</data-lsp>)</span><span style="color: #D32F2F">}</span><span style="color: #22863A">`</span><span style="color: #24292EFF">;</span></div><div class="line"><span style="color: #24292EFF">      } </span><span style="color: #D32F2F">else</span><span style="color: #24292EFF"> {</span></div><div class="line"><span style="color: #24292EFF">        <data-lsp lsp="let body: string | undefined">body</data-lsp> </span><span style="color: #D32F2F">=</span><span style="color: #24292EFF"> <data-lsp lsp="const stringifiedInput: string">stringifiedInput</data-lsp>;</span></div><div class="line"><span style="color: #24292EFF">      }</span></div><div class="line"><span style="color: #24292EFF">    }</span></div><div class="line">&nbsp;</div><div class="line"><span style="color: #24292EFF">    </span><span style="color: #D32F2F">const</span><span style="color: #24292EFF"> </span><span style="color: #1976D2"><data-lsp lsp="const json: TRPCResponse">json</data-lsp></span><span style="color: #D32F2F">:</span><span style="color: #24292EFF"> </span><span style="color: #6F42C1"><data-lsp lsp="(alias) type TRPCResponse<TData = unknown, TError extends TRPCErrorShape = TRPCErrorShape<object>> = TRPCErrorResponse<TError> | TRPCSuccessResponse<TData>
import TRPCResponse">TRPCResponse</data-lsp></span><span style="color: #24292EFF"> </span><span style="color: #D32F2F">=</span><span style="color: #24292EFF"> </span><span style="color: #D32F2F">await</span><span style="color: #24292EFF"> </span><span style="color: #6F42C1"><data-lsp lsp="function fetch(input: RequestInfo | URL, init?: RequestInit): Promise<Response>">fetch</data-lsp></span><span style="color: #24292EFF">(<data-lsp lsp="let uri: string">uri</data-lsp></span><span style="color: #212121">,</span><span style="color: #24292EFF"> {</span></div><div class="line"><span style="color: #24292EFF">      <data-lsp lsp="(property) RequestInit.method?: string | undefined">method</data-lsp></span><span style="color: #D32F2F">:</span><span style="color: #24292EFF"> <data-lsp lsp="const method: &quot;query&quot; | &quot;mutate&quot;">method</data-lsp> </span><span style="color: #D32F2F">===</span><span style="color: #24292EFF"> </span><span style="color: #22863A">'query'</span><span style="color: #24292EFF"> </span><span style="color: #D32F2F">?</span><span style="color: #24292EFF"> </span><span style="color: #22863A">'GET'</span><span style="color: #24292EFF"> </span><span style="color: #D32F2F">:</span><span style="color: #24292EFF"> </span><span style="color: #22863A">'POST'</span><span style="color: #212121">,</span></div><div class="line"><span style="color: #24292EFF">      <data-lsp lsp="(property) RequestInit.headers?: HeadersInit | undefined">headers</data-lsp></span><span style="color: #D32F2F">:</span><span style="color: #24292EFF"> {</span></div><div class="line"><span style="color: #24292EFF">        </span><span style="color: #22863A">'Content-Type'</span><span style="color: #D32F2F">:</span><span style="color: #24292EFF"> </span><span style="color: #22863A">'application/json'</span><span style="color: #212121">,</span></div><div class="line"><span style="color: #24292EFF">      }</span><span style="color: #212121">,</span></div><div class="line"><span style="color: #24292EFF">      <data-lsp lsp="(property) RequestInit.body?: BodyInit | null | undefined">body</data-lsp></span><span style="color: #212121">,</span></div><div class="line"><span style="color: #24292EFF">    })</span><span style="color: #6F42C1">.<data-lsp lsp="(method) Promise<Response>.then<any, TRPCErrorResponse<TRPCErrorShape<object>> | TRPCSuccessResponse<unknown>>(onfulfilled?: ((value: Response) => any) | null | undefined, onrejected?: ((reason: any) => TRPCErrorResponse<TRPCErrorShape<object>> | TRPCSuccessResponse<unknown> | PromiseLike<TRPCErrorResponse<TRPCErrorShape<object>> | TRPCSuccessResponse<unknown>>) | null | undefined): Promise<...>">then</data-lsp></span><span style="color: #24292EFF">((<data-lsp lsp="(parameter) res: Response">res</data-lsp>) </span><span style="color: #D32F2F">=&gt;</span><span style="color: #24292EFF"> </span><span style="color: #1976D2"><data-lsp lsp="(parameter) res: Response">res</data-lsp></span><span style="color: #6F42C1">.<data-lsp lsp="(method) Body.json(): Promise<any>">json</data-lsp></span><span style="color: #24292EFF">());</span></div><div class="line">&nbsp;</div><div class="line"><span style="color: #24292EFF">    </span><span style="color: #D32F2F">if</span><span style="color: #24292EFF"> (</span><span style="color: #22863A">'error'</span><span style="color: #24292EFF"> </span><span style="color: #D32F2F">in</span><span style="color: #24292EFF"> <data-lsp lsp="const json: TRPCResponse">json</data-lsp>) {</span></div><div class="line"><span style="color: #24292EFF">      </span><span style="color: #D32F2F">throw</span><span style="color: #24292EFF"> </span><span style="color: #D32F2F">new</span><span style="color: #24292EFF"> </span><span style="color: #1976D2"><data-lsp lsp="var Error: ErrorConstructor
new (message?: string) => Error">Error</data-lsp></span><span style="color: #24292EFF">(</span><span style="color: #22863A">`Error: </span><span style="color: #D32F2F">${</span><span style="color: #1976D2"><data-lsp lsp="const json: TRPCErrorResponse<TRPCErrorShape<object>>">json</data-lsp></span><span style="color: #24292EFF">.</span><span style="color: #1976D2"><data-lsp lsp="(property) JSONRPC2.ErrorResponse<TRPCErrorShape<object>>.error: TRPCErrorShape<object>">error</data-lsp></span><span style="color: #24292EFF">.<data-lsp lsp="(property) TRPCErrorShape<object>.message: string">message</data-lsp></span><span style="color: #D32F2F">}</span><span style="color: #22863A">`</span><span style="color: #24292EFF">);</span></div><div class="line"><span style="color: #24292EFF">    }</span></div><div class="line"><span style="color: #24292EFF">    </span><span style="color: #C2C3C5">// No error - all good. Return the data.</span></div><div class="line"><span style="color: #24292EFF">    </span><span style="color: #D32F2F">return</span><span style="color: #24292EFF"> </span><span style="color: #1976D2"><data-lsp lsp="const json: TRPCSuccessResponse<unknown>">json</data-lsp></span><span style="color: #24292EFF">.</span><span style="color: #1976D2"><data-lsp lsp="(property) JSONRPC2.ResultResponse<TRPCResult<unknown>>.result: TRPCResult<unknown>">result</data-lsp></span><span style="color: #24292EFF">.<data-lsp lsp="(property) TRPCResult<unknown>.data: unknown">data</data-lsp>;</span></div><div class="line"><span style="color: #24292EFF">  }</span><span style="color: #212121">,</span><span style="color: #24292EFF"> []) </span><span style="color: #D32F2F">as</span><span style="color: #24292EFF"> </span><span style="color: #6F42C1"><data-lsp lsp="type DecorateRouterRecord<TRecord extends TRPCRouterRecord> = { [TKey in keyof TRecord]: TRecord[TKey] extends infer $Value ? $Value extends TRPCRouterRecord ? DecorateRouterRecord<$Value> : $Value extends AnyTRPCProcedure ? DecorateProcedure<$Value> : never : never; }">DecorateRouterRecord</data-lsp></span><span style="color: #24292EFF">&lt;</span><span style="color: #6F42C1"><data-lsp lsp="(type parameter) TRouter in <TRouter extends AnyTRPCRouter>(baseUrl: string): DecorateRouterRecord<TRouter[&quot;_def&quot;][&quot;record&quot;]>">TRouter</data-lsp></span><span style="color: #24292EFF">[</span><span style="color: #22863A">'_def'</span><span style="color: #24292EFF">][</span><span style="color: #22863A">'record'</span><span style="color: #24292EFF">]&gt;;</span></div><div class="line"><span style="color: #C2C3C5">//   ^? provide empty array as path to begin with</span></div></code></div><button type="button" aria-label="Copy code to clipboard" class="copy-button" onclick="navigator.clipboard.writeText(this.previousSibling.innerText),this.classList.add(&quot;copied&quot;),this.textContent=&quot;Copied&quot;,setTimeout(()=>{this.classList.remove(&quot;copied&quot;),this.textContent=&quot;Copy&quot;},2e3)">Copy</button></pre>
<pre class="shiki github-dark twoslash lsp" style="background-color: #0d1117; color: #c9d1d9"><div class="language-id">ts</div><div class="code-container"><code><div class="line"><span style="color: #FF7B72">import</span><span style="color: #C9D1D9"> { <data-lsp lsp="(alias) type TRPCResponse<TData = unknown, TError extends TRPCErrorShape = TRPCErrorShape<object>> = TRPCErrorResponse<TError> | TRPCSuccessResponse<TData>
import TRPCResponse">TRPCResponse</data-lsp> } </span><span style="color: #FF7B72">from</span><span style="color: #C9D1D9"> </span><span style="color: #A5D6FF">'@trpc/server/rpc'</span><span style="color: #C9D1D9">;</span></div><div class="line">&nbsp;</div><div class="line"><span style="color: #FF7B72">export</span><span style="color: #C9D1D9"> </span><span style="color: #FF7B72">const</span><span style="color: #C9D1D9"> </span><span style="color: #D2A8FF"><data-lsp lsp="const createTinyRPCClient: <TRouter extends AnyTRPCRouter>(baseUrl: string) => DecorateRouterRecord<TRouter[&quot;_def&quot;][&quot;record&quot;]>">createTinyRPCClient</data-lsp></span><span style="color: #C9D1D9"> </span><span style="color: #FF7B72">=</span><span style="color: #C9D1D9"> &lt;</span><span style="color: #FFA657"><data-lsp lsp="(type parameter) TRouter in <TRouter extends AnyTRPCRouter>(baseUrl: string): DecorateRouterRecord<TRouter[&quot;_def&quot;][&quot;record&quot;]>">TRouter</data-lsp></span><span style="color: #C9D1D9"> </span><span style="color: #FF7B72">extends</span><span style="color: #C9D1D9"> </span><span style="color: #FFA657"><data-lsp lsp="(alias) type AnyTRPCRouter = Router<any, any>
import AnyTRPCRouter">AnyTRPCRouter</data-lsp></span><span style="color: #C9D1D9">&gt;(</span></div><div class="line"><span style="color: #C9D1D9">  </span><span style="color: #FFA657"><data-lsp lsp="(parameter) baseUrl: string">baseUrl</data-lsp></span><span style="color: #FF7B72">:</span><span style="color: #C9D1D9"> </span><span style="color: #79C0FF">string</span><span style="color: #C9D1D9">,</span></div><div class="line"><span style="color: #C9D1D9">) </span><span style="color: #FF7B72">=&gt;</span></div><div class="line"><span style="color: #C9D1D9">  </span><span style="color: #D2A8FF"><data-lsp lsp="function createRecursiveProxy(callback: ProxyCallback, path: readonly string[]): unknown">createRecursiveProxy</data-lsp></span><span style="color: #C9D1D9">(</span><span style="color: #FF7B72">async</span><span style="color: #C9D1D9"> (</span><span style="color: #FFA657"><data-lsp lsp="(parameter) opts: ProxyCallbackOptions">opts</data-lsp></span><span style="color: #C9D1D9">) </span><span style="color: #FF7B72">=&gt;</span><span style="color: #C9D1D9"> {</span></div><div class="line"><span style="color: #C9D1D9">    </span><span style="color: #FF7B72">const</span><span style="color: #C9D1D9"> </span><span style="color: #79C0FF"><data-lsp lsp="const path: string[]">path</data-lsp></span><span style="color: #C9D1D9"> </span><span style="color: #FF7B72">=</span><span style="color: #C9D1D9"> [</span><span style="color: #FF7B72">...</span><span style="color: #C9D1D9"><data-lsp lsp="(parameter) opts: ProxyCallbackOptions">opts</data-lsp>.<data-lsp lsp="(property) ProxyCallbackOptions.path: readonly string[]">path</data-lsp>]; </span><span style="color: #8B949E">// e.g. ["post", "byId", "query"]</span></div><div class="line"><span style="color: #C9D1D9">    </span><span style="color: #FF7B72">const</span><span style="color: #C9D1D9"> </span><span style="color: #79C0FF"><data-lsp lsp="const method: &quot;query&quot; | &quot;mutate&quot;">method</data-lsp></span><span style="color: #C9D1D9"> </span><span style="color: #FF7B72">=</span><span style="color: #C9D1D9"> <data-lsp lsp="const path: string[]">path</data-lsp>.</span><span style="color: #D2A8FF"><data-lsp lsp="(method) Array<string>.pop(): string | undefined">pop</data-lsp></span><span style="color: #C9D1D9">()</span><span style="color: #FF7B72">!</span><span style="color: #C9D1D9"> </span><span style="color: #FF7B72">as</span><span style="color: #C9D1D9"> </span><span style="color: #A5D6FF">'query'</span><span style="color: #C9D1D9"> </span><span style="color: #FF7B72">|</span><span style="color: #C9D1D9"> </span><span style="color: #A5D6FF">'mutate'</span><span style="color: #C9D1D9">;</span></div><div class="line"><span style="color: #C9D1D9">    </span><span style="color: #FF7B72">const</span><span style="color: #C9D1D9"> </span><span style="color: #79C0FF"><data-lsp lsp="const dotPath: string">dotPath</data-lsp></span><span style="color: #C9D1D9"> </span><span style="color: #FF7B72">=</span><span style="color: #C9D1D9"> <data-lsp lsp="const path: string[]">path</data-lsp>.</span><span style="color: #D2A8FF"><data-lsp lsp="(method) Array<string>.join(separator?: string): string">join</data-lsp></span><span style="color: #C9D1D9">(</span><span style="color: #A5D6FF">'.'</span><span style="color: #C9D1D9">); </span><span style="color: #8B949E">// "post.byId" - this is the path procedures have on the backend</span></div><div class="line"><span style="color: #C9D1D9">    </span><span style="color: #FF7B72">let</span><span style="color: #C9D1D9"> <data-lsp lsp="let uri: string">uri</data-lsp> </span><span style="color: #FF7B72">=</span><span style="color: #C9D1D9"> </span><span style="color: #A5D6FF">`${</span><span style="color: #C9D1D9"><data-lsp lsp="(parameter) baseUrl: string">baseUrl</data-lsp></span><span style="color: #A5D6FF">}/${</span><span style="color: #C9D1D9"><data-lsp lsp="const dotPath: string">dotPath</data-lsp></span><span style="color: #A5D6FF">}`</span><span style="color: #C9D1D9">;</span></div><div class="line">&nbsp;</div><div class="line"><span style="color: #C9D1D9">    </span><span style="color: #FF7B72">const</span><span style="color: #C9D1D9"> [</span><span style="color: #79C0FF"><data-lsp lsp="const input: unknown">input</data-lsp></span><span style="color: #C9D1D9">] </span><span style="color: #FF7B72">=</span><span style="color: #C9D1D9"> <data-lsp lsp="(parameter) opts: ProxyCallbackOptions">opts</data-lsp>.<data-lsp lsp="(property) ProxyCallbackOptions.args: readonly unknown[]">args</data-lsp>;</span></div><div class="line"><span style="color: #C9D1D9">    </span><span style="color: #FF7B72">const</span><span style="color: #C9D1D9"> </span><span style="color: #79C0FF"><data-lsp lsp="const stringifiedInput: string | false">stringifiedInput</data-lsp></span><span style="color: #C9D1D9"> </span><span style="color: #FF7B72">=</span><span style="color: #C9D1D9"> <data-lsp lsp="const input: unknown">input</data-lsp> </span><span style="color: #FF7B72">!==</span><span style="color: #C9D1D9"> </span><span style="color: #79C0FF"><data-lsp lsp="var undefined">undefined</data-lsp></span><span style="color: #C9D1D9"> </span><span style="color: #FF7B72">&amp;&amp;</span><span style="color: #C9D1D9"> </span><span style="color: #79C0FF"><data-lsp lsp="var JSON: JSON">JSON</data-lsp></span><span style="color: #C9D1D9">.</span><span style="color: #79C0FF"><data-lsp lsp="(method) JSON.stringify(value: any, replacer?: (this: any, key: string, value: any) => any, space?: string | number): string (+1 overload)">stringify</data-lsp></span><span style="color: #C9D1D9">(<data-lsp lsp="const input: {} | null">input</data-lsp>);</span></div><div class="line"><span style="color: #C9D1D9">    </span><span style="color: #FF7B72">let</span><span style="color: #C9D1D9"> <data-lsp lsp="let body: string | undefined">body</data-lsp></span><span style="color: #FF7B72">:</span><span style="color: #C9D1D9"> </span><span style="color: #79C0FF">undefined</span><span style="color: #C9D1D9"> </span><span style="color: #FF7B72">|</span><span style="color: #C9D1D9"> </span><span style="color: #79C0FF">string</span><span style="color: #C9D1D9"> </span><span style="color: #FF7B72">=</span><span style="color: #C9D1D9"> </span><span style="color: #79C0FF"><data-lsp lsp="var undefined">undefined</data-lsp></span><span style="color: #C9D1D9">;</span></div><div class="line"><span style="color: #C9D1D9">    </span><span style="color: #FF7B72">if</span><span style="color: #C9D1D9"> (<data-lsp lsp="const stringifiedInput: string | false">stringifiedInput</data-lsp> </span><span style="color: #FF7B72">!==</span><span style="color: #C9D1D9"> </span><span style="color: #79C0FF">false</span><span style="color: #C9D1D9">) {</span></div><div class="line"><span style="color: #C9D1D9">      </span><span style="color: #FF7B72">if</span><span style="color: #C9D1D9"> (<data-lsp lsp="const method: &quot;query&quot; | &quot;mutate&quot;">method</data-lsp> </span><span style="color: #FF7B72">===</span><span style="color: #C9D1D9"> </span><span style="color: #A5D6FF">'query'</span><span style="color: #C9D1D9">) {</span></div><div class="line"><span style="color: #C9D1D9">        <data-lsp lsp="let uri: string">uri</data-lsp> </span><span style="color: #FF7B72">+=</span><span style="color: #C9D1D9"> </span><span style="color: #A5D6FF">`?input=${</span><span style="color: #79C0FF"><data-lsp lsp="function encodeURIComponent(uriComponent: string | number | boolean): string">encodeURIComponent</data-lsp></span><span style="color: #A5D6FF">(</span><span style="color: #C9D1D9"><data-lsp lsp="const stringifiedInput: string">stringifiedInput</data-lsp></span><span style="color: #A5D6FF">)</span><span style="color: #A5D6FF">}`</span><span style="color: #C9D1D9">;</span></div><div class="line"><span style="color: #C9D1D9">      } </span><span style="color: #FF7B72">else</span><span style="color: #C9D1D9"> {</span></div><div class="line"><span style="color: #C9D1D9">        <data-lsp lsp="let body: string | undefined">body</data-lsp> </span><span style="color: #FF7B72">=</span><span style="color: #C9D1D9"> <data-lsp lsp="const stringifiedInput: string">stringifiedInput</data-lsp>;</span></div><div class="line"><span style="color: #C9D1D9">      }</span></div><div class="line"><span style="color: #C9D1D9">    }</span></div><div class="line">&nbsp;</div><div class="line"><span style="color: #C9D1D9">    </span><span style="color: #FF7B72">const</span><span style="color: #C9D1D9"> </span><span style="color: #79C0FF"><data-lsp lsp="const json: TRPCResponse">json</data-lsp></span><span style="color: #FF7B72">:</span><span style="color: #C9D1D9"> </span><span style="color: #FFA657"><data-lsp lsp="(alias) type TRPCResponse<TData = unknown, TError extends TRPCErrorShape = TRPCErrorShape<object>> = TRPCErrorResponse<TError> | TRPCSuccessResponse<TData>
import TRPCResponse">TRPCResponse</data-lsp></span><span style="color: #C9D1D9"> </span><span style="color: #FF7B72">=</span><span style="color: #C9D1D9"> </span><span style="color: #FF7B72">await</span><span style="color: #C9D1D9"> </span><span style="color: #D2A8FF"><data-lsp lsp="function fetch(input: RequestInfo | URL, init?: RequestInit): Promise<Response>">fetch</data-lsp></span><span style="color: #C9D1D9">(<data-lsp lsp="let uri: string">uri</data-lsp>, {</span></div><div class="line"><span style="color: #C9D1D9">      <data-lsp lsp="(property) RequestInit.method?: string | undefined">method</data-lsp>: <data-lsp lsp="const method: &quot;query&quot; | &quot;mutate&quot;">method</data-lsp> </span><span style="color: #FF7B72">===</span><span style="color: #C9D1D9"> </span><span style="color: #A5D6FF">'query'</span><span style="color: #C9D1D9"> </span><span style="color: #FF7B72">?</span><span style="color: #C9D1D9"> </span><span style="color: #A5D6FF">'GET'</span><span style="color: #C9D1D9"> </span><span style="color: #FF7B72">:</span><span style="color: #C9D1D9"> </span><span style="color: #A5D6FF">'POST'</span><span style="color: #C9D1D9">,</span></div><div class="line"><span style="color: #C9D1D9">      <data-lsp lsp="(property) RequestInit.headers?: HeadersInit | undefined">headers</data-lsp>: {</span></div><div class="line"><span style="color: #C9D1D9">        </span><span style="color: #A5D6FF">'Content-Type'</span><span style="color: #C9D1D9">: </span><span style="color: #A5D6FF">'application/json'</span><span style="color: #C9D1D9">,</span></div><div class="line"><span style="color: #C9D1D9">      },</span></div><div class="line"><span style="color: #C9D1D9">      <data-lsp lsp="(property) RequestInit.body?: BodyInit | null | undefined">body</data-lsp>,</span></div><div class="line"><span style="color: #C9D1D9">    }).</span><span style="color: #D2A8FF"><data-lsp lsp="(method) Promise<Response>.then<any, TRPCErrorResponse<TRPCErrorShape<object>> | TRPCSuccessResponse<unknown>>(onfulfilled?: ((value: Response) => any) | null | undefined, onrejected?: ((reason: any) => TRPCErrorResponse<TRPCErrorShape<object>> | TRPCSuccessResponse<unknown> | PromiseLike<TRPCErrorResponse<TRPCErrorShape<object>> | TRPCSuccessResponse<unknown>>) | null | undefined): Promise<...>">then</data-lsp></span><span style="color: #C9D1D9">((</span><span style="color: #FFA657"><data-lsp lsp="(parameter) res: Response">res</data-lsp></span><span style="color: #C9D1D9">) </span><span style="color: #FF7B72">=&gt;</span><span style="color: #C9D1D9"> <data-lsp lsp="(parameter) res: Response">res</data-lsp>.</span><span style="color: #D2A8FF"><data-lsp lsp="(method) Body.json(): Promise<any>">json</data-lsp></span><span style="color: #C9D1D9">());</span></div><div class="line">&nbsp;</div><div class="line"><span style="color: #C9D1D9">    </span><span style="color: #FF7B72">if</span><span style="color: #C9D1D9"> (</span><span style="color: #A5D6FF">'error'</span><span style="color: #C9D1D9"> </span><span style="color: #FF7B72">in</span><span style="color: #C9D1D9"> <data-lsp lsp="const json: TRPCResponse">json</data-lsp>) {</span></div><div class="line"><span style="color: #C9D1D9">      </span><span style="color: #FF7B72">throw</span><span style="color: #C9D1D9"> </span><span style="color: #FF7B72">new</span><span style="color: #C9D1D9"> </span><span style="color: #79C0FF"><data-lsp lsp="var Error: ErrorConstructor
new (message?: string) => Error">Error</data-lsp></span><span style="color: #C9D1D9">(</span><span style="color: #A5D6FF">`Error: ${</span><span style="color: #C9D1D9"><data-lsp lsp="const json: TRPCErrorResponse<TRPCErrorShape<object>>">json</data-lsp></span><span style="color: #A5D6FF">.</span><span style="color: #C9D1D9"><data-lsp lsp="(property) JSONRPC2.ErrorResponse<TRPCErrorShape<object>>.error: TRPCErrorShape<object>">error</data-lsp></span><span style="color: #A5D6FF">.</span><span style="color: #C9D1D9"><data-lsp lsp="(property) TRPCErrorShape<object>.message: string">message</data-lsp></span><span style="color: #A5D6FF">}`</span><span style="color: #C9D1D9">);</span></div><div class="line"><span style="color: #C9D1D9">    }</span></div><div class="line"><span style="color: #C9D1D9">    </span><span style="color: #8B949E">// No error - all good. Return the data.</span></div><div class="line"><span style="color: #C9D1D9">    </span><span style="color: #FF7B72">return</span><span style="color: #C9D1D9"> <data-lsp lsp="const json: TRPCSuccessResponse<unknown>">json</data-lsp>.<data-lsp lsp="(property) JSONRPC2.ResultResponse<TRPCResult<unknown>>.result: TRPCResult<unknown>">result</data-lsp>.<data-lsp lsp="(property) TRPCResult<unknown>.data: unknown">data</data-lsp>;</span></div><div class="line"><span style="color: #C9D1D9">  }, []) </span><span style="color: #FF7B72">as</span><span style="color: #C9D1D9"> </span><span style="color: #FFA657"><data-lsp lsp="type DecorateRouterRecord<TRecord extends TRPCRouterRecord> = { [TKey in keyof TRecord]: TRecord[TKey] extends infer $Value ? $Value extends TRPCRouterRecord ? DecorateRouterRecord<$Value> : $Value extends AnyTRPCProcedure ? DecorateProcedure<$Value> : never : never; }">DecorateRouterRecord</data-lsp></span><span style="color: #C9D1D9">&lt;</span><span style="color: #FFA657"><data-lsp lsp="(type parameter) TRouter in <TRouter extends AnyTRPCRouter>(baseUrl: string): DecorateRouterRecord<TRouter[&quot;_def&quot;][&quot;record&quot;]>">TRouter</data-lsp></span><span style="color: #C9D1D9">[</span><span style="color: #A5D6FF">'_def'</span><span style="color: #C9D1D9">][</span><span style="color: #A5D6FF">'record'</span><span style="color: #C9D1D9">]&gt;;</span></div><div class="line"><span style="color: #8B949E">//   ^? provide empty array as path to begin with</span></div></code></div><button type="button" aria-label="Copy code to clipboard" class="copy-button" onclick="navigator.clipboard.writeText(this.previousSibling.innerText),this.classList.add(&quot;copied&quot;),this.textContent=&quot;Copied&quot;,setTimeout(()=>{this.classList.remove(&quot;copied&quot;),this.textContent=&quot;Copy&quot;},2e3)">Copy</button></pre></div>
<p>Det viktigaste här är att vår sökväg är separerad med <code>.</code> istället för <code>/</code>. Detta låter oss ha en enda API-hanterare på servern som behandlar alla förfrågningar, istället för en per procedur. Om du använder ett ramverk med filbaserad routing som Next.js, känner du igen catchall-filen <code>/api/trpc/[trpc].ts</code> som matchar alla procedursökvägar.</p>
<p>Vi har också en <code>TRPCResponse</code>-typannotering på <code>fetch</code>-förfrågan. Den definierar det JSONRPC-kompatibla svarsformat som servern returnerar. Du kan läsa mer om det <a href="https://trpc.io/docs/rpc" target="_blank" rel="noopener noreferrer">här</a>. TL;DR: vi får tillbaka antingen ett <code>result</code> eller ett <code>error</code>-objekt som vi kan använda för att avgöra om förfrågan lyckades eller inte och hantera eventuella fel på lämpligt sätt.</p>
<p>Och det var allt! Detta är all kod du behöver för att anropa dina tRPC-procedurer på klienten som om de vore lokala funktioner. På ytan ser det ut som att vi bara anropar <code>publicProcedure.query / mutation</code>'s resolver-funktion via normal egenskapsåtkomst, men i verkligheten korsar vi ett nätverksgräns så att vi kan använda serverbibliotek som Prisma utan att läcka databasuppgifter.</p>
<h2 class="anchor anchorWithStickyNavbar_MYIC" id="testa-det-själv">Testa det själv!<a href="https://trpc.io/sv/blog/tinyrpc-client#testa-det-sj%C3%A4lv" class="hash-link" aria-label="Direktlänk till Testa det själv!" title="Direktlänk till Testa det själv!">​</a></h2>
<p>Skapa nu klienten och ange din servers URL – du får fullständig autofyllning och typsäkerhet när du anropar dina procedurer!</p>
<div><pre class="shiki min-light" style="background-color: #ffffff; color: #24292eff"><div class="language-id">ts</div><div class="code-container"><code><div class="line"><span style="color: #D32F2F">const</span><span style="color: #24292EFF"> </span><span style="color: #1976D2">url</span><span style="color: #24292EFF"> </span><span style="color: #D32F2F">=</span><span style="color: #24292EFF"> </span><span style="color: #22863A">'http://localhost:3000/api/trpc'</span><span style="color: #24292EFF">;</span></div><div class="line"><span style="color: #D32F2F">const</span><span style="color: #24292EFF"> </span><span style="color: #1976D2">client</span><span style="color: #24292EFF"> </span><span style="color: #D32F2F">=</span><span style="color: #24292EFF"> </span><span style="color: #6F42C1">createTinyRPCClient</span><span style="color: #24292EFF">&lt;</span><span style="color: #6F42C1">AppRouter</span><span style="color: #24292EFF">&gt;(url);</span></div><div class="line"></div><div class="line"><span style="color: #C2C3C5">// 🧙‍♀️ magic autocompletion</span></div><div class="line"></div><div class="line"><span style="color: #1976D2">client</span><span style="color: #24292EFF">.</span><span style="color: #1976D2">post</span><span style="color: #24292EFF">.b;</span></div><div class="line"></div><div class="line"><span style="color: #C2C3C5">// 👀 fully typesafe</span></div><div class="line"><span style="color: #D32F2F">const</span><span style="color: #24292EFF"> </span><span style="color: #1976D2">post</span><span style="color: #24292EFF"> </span><span style="color: #D32F2F">=</span><span style="color: #24292EFF"> </span><span style="color: #D32F2F">await</span><span style="color: #24292EFF"> </span><span style="color: #1976D2">client</span><span style="color: #6F42C1">.</span><span style="color: #1976D2">post</span><span style="color: #6F42C1">.</span><span style="color: #1976D2">byId</span><span style="color: #6F42C1">.query</span><span style="color: #24292EFF">({ id</span><span style="color: #D32F2F">:</span><span style="color: #24292EFF"> </span><span style="color: #22863A">'123'</span><span style="color: #24292EFF"> });</span></div></code></div><button type="button" aria-label="Copy code to clipboard" class="copy-button" onclick="navigator.clipboard.writeText(this.previousSibling.innerText),this.classList.add(&quot;copied&quot;),this.textContent=&quot;Copied&quot;,setTimeout(()=>{this.classList.remove(&quot;copied&quot;),this.textContent=&quot;Copy&quot;},2e3)">Copy</button></pre>
<pre class="shiki github-dark" style="background-color: #0d1117; color: #c9d1d9"><div class="language-id">ts</div><div class="code-container"><code><div class="line"><span style="color: #FF7B72">const</span><span style="color: #C9D1D9"> </span><span style="color: #79C0FF">url</span><span style="color: #C9D1D9"> </span><span style="color: #FF7B72">=</span><span style="color: #C9D1D9"> </span><span style="color: #A5D6FF">'http://localhost:3000/api/trpc'</span><span style="color: #C9D1D9">;</span></div><div class="line"><span style="color: #FF7B72">const</span><span style="color: #C9D1D9"> </span><span style="color: #79C0FF">client</span><span style="color: #C9D1D9"> </span><span style="color: #FF7B72">=</span><span style="color: #C9D1D9"> </span><span style="color: #D2A8FF">createTinyRPCClient</span><span style="color: #C9D1D9">&lt;</span><span style="color: #FFA657">AppRouter</span><span style="color: #C9D1D9">&gt;(url);</span></div><div class="line"></div><div class="line"><span style="color: #8B949E">// 🧙‍♀️ magic autocompletion</span></div><div class="line"></div><div class="line"><span style="color: #C9D1D9">client.post.b;</span></div><div class="line"></div><div class="line"><span style="color: #8B949E">// 👀 fully typesafe</span></div><div class="line"><span style="color: #FF7B72">const</span><span style="color: #C9D1D9"> </span><span style="color: #79C0FF">post</span><span style="color: #C9D1D9"> </span><span style="color: #FF7B72">=</span><span style="color: #C9D1D9"> </span><span style="color: #FF7B72">await</span><span style="color: #C9D1D9"> client.post.byId.</span><span style="color: #D2A8FF">query</span><span style="color: #C9D1D9">({ id: </span><span style="color: #A5D6FF">'123'</span><span style="color: #C9D1D9"> });</span></div></code></div><button type="button" aria-label="Copy code to clipboard" class="copy-button" onclick="navigator.clipboard.writeText(this.previousSibling.innerText),this.classList.add(&quot;copied&quot;),this.textContent=&quot;Copied&quot;,setTimeout(()=>{this.classList.remove(&quot;copied&quot;),this.textContent=&quot;Copy&quot;},2e3)">Copy</button></pre></div>
<p>Fullständig källkod för klienten finns <a href="https://github.com/trpc/trpc/blob/main/packages/tests/showcase/tinyrpc.ts" target="_blank" rel="noopener noreferrer">här</a>, och tester som visar användning <a href="https://github.com/trpc/trpc/blob/main/packages/tests/showcase/tinyrpc.test.ts" target="_blank" rel="noopener noreferrer">här</a>.</p>
<h2 class="anchor anchorWithStickyNavbar_MYIC" id="avslutning">Avslutning<a href="https://trpc.io/sv/blog/tinyrpc-client#avslutning" class="hash-link" aria-label="Direktlänk till Avslutning" title="Direktlänk till Avslutning">​</a></h2>
<p>Jag hoppas du gillade artikeln och lärde dig något om hur tRPC fungerar. Du bör förmodligen inte använda denna lösning i stället för @trpc/client som bara är några få KB större – den erbjuder mycket mer flexibilitet än vad vi visat här:</p>
<ul>
<li>
<p>Frågealternativ för abort-signaler, SSR m.m.</p>
</li>
<li>
<p>Länkar</p>
</li>
<li>
<p>Batchbearbetning av procedurer</p>
</li>
<li>
<p>WebSockets / prenumerationer</p>
</li>
<li>
<p>Bra felhantering</p>
</li>
<li>
<p>Datatransformatorer</p>
</li>
<li>
<p>Hantering av specialfall som när responsen inte följer tRPC-standarden</p>
</li>
</ul>
<p>Vi täckte inte heller serversidan idag, det får kanske bli ett framtida inlägg. Har du frågor är du välkommen att höra av dig till mig på <a href="https://twitter.com/jullerino" target="_blank" rel="noopener noreferrer">Twitter</a>.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[TypeScript-prestandalektioner vid omstrukturering för v10]]></title>
            <link>https://trpc.io/sv/blog/typescript-performance-lessons</link>
            <guid>https://trpc.io/sv/blog/typescript-performance-lessons</guid>
            <pubDate>Sat, 14 Jan 2023 00:00:00 GMT</pubDate>
            <description><![CDATA[Denna sida har översatts av PageTurner AI (beta). Inte officiellt godkänd av projektet.]]></description>
            <content:encoded><![CDATA[<div class="theme-admonition theme-admonition-info admonition_v5Ag alert alert--info"><div class="admonitionHeading_usrK"><span class="admonitionIcon_bgEp"><svg viewBox="0 0 14 16"><path fill-rule="evenodd" d="M7 2.3c3.14 0 5.7 2.56 5.7 5.7s-2.56 5.7-5.7 5.7A5.71 5.71 0 0 1 1.3 8c0-3.14 2.56-5.7 5.7-5.7zM7 1C3.14 1 0 4.14 0 8s3.14 7 7 7 7-3.14 7-7-3.14-7-7-7zm1 3H6v5h2V4zm0 6H6v2h2v-2z"></path></svg></span>Inofficiell Beta-översättning</div><div class="admonitionContent_e2NW"><p>Denna sida har översatts av <a href="https://page-turner.com/" target="_blank" rel="noopener noreferrer"><strong>PageTurner</strong></a> AI (beta). Inte officiellt godkänd av projektet.
Hittade du ett fel? <a href="https://feedback.page-turner.com/?repo_id=683d130a-1828-4b22-91cd-ef2d269ef3f5&amp;file_path=blog%2F2023-01-14-typescript-performance-lessons.mdx&amp;locale=sv" target="_blank" rel="noopener noreferrer">Rapportera problem →</a></p></div></div>
<p>Det är ingen hemlighet att TypeScript är drivkraften bakom hur tRPC levererar sin fantastiska DX. Att använda TypeScript är modern standard för att skapa bra JavaScript-baserade upplevelser idag – men denna ökade typsäkerhet har sina avvägningar.</p>
<!-- -->
<p>Det är ingen hemlighet att TypeScript är drivkraften bakom hur tRPC levererar sin fantastiska utvecklarupplevelse. Att använda TypeScript är den moderna standarden för att leverera bra JavaScript-baserade upplevelser idag – men denna förbättrade typsäkerhet medför vissa kompromisser.</p>
<p>Idag är TypeScripts typkontroller benägen att bli långsam (även om releaser som <a href="https://devblogs.microsoft.com/typescript/announcing-typescript-4-9/#performance-improvements" target="_blank" rel="noopener noreferrer">TS 4.9</a> är lovande!). Bibliotek innehåller nästan alltid de mest avancerade TypeScript-besvärjelserna i din kodbas, vilket pressar din TS-kompilator till sina gränser. Av den anledningen måste biblioteksförfattare som vi vara medvetna om vårt bidrag till denna börda och göra vårt bästa för att hålla din IDE så snabb som möjligt.</p>
<h2 class="anchor anchorWithStickyNavbar_MYIC" id="automatisera-bibliotekets-prestanda">Automatisera bibliotekets prestanda<a href="https://trpc.io/sv/blog/typescript-performance-lessons#automatisera-bibliotekets-prestanda" class="hash-link" aria-label="Direktlänk till Automatisera bibliotekets prestanda" title="Direktlänk till Automatisera bibliotekets prestanda">​</a></h2>
<p>Medan tRPC var i <code>v9</code> började vi se rapporter från utvecklare om att deras stora tRPC-routrar började få skadliga effekter på deras typkontroll. Detta var en ny upplevelse för tRPC eftersom vi såg en enorm adoption under <code>v9</code>-fasen av tRPC<!-- -->:s<!-- --> utveckling. Med fler utvecklare som skapar allt större produkter med tRPC började vissa brister att visa sig.</p>
<p>Ditt bibliotek kanske inte är långsamt <em>just nu</em>, men det är viktigt att hålla ett öga på prestanda när ditt bibliotek växer och förändras. Automatiserade tester kan avlasta dig avsevärt när du skapar bibliotek (och bygger applikationer!) genom att <em>programmatiskt</em> testa din bibliotekskod vid varje incheckning.</p>
<p>För tRPC gör vi vårt bästa för att säkerställa detta genom att <a href="https://github.com/trpc/trpc/blob/9fc2d06a8924da73e10b9d4497f3a1f53de706ed/scripts/generateBigBoi.ts" target="_blank" rel="noopener noreferrer">generera</a> och <a href="https://github.com/trpc/trpc/blob/9fc2d06a8924da73e10b9d4497f3a1f53de706ed/packages/tests/server/react/bigBoi.test.tsx" target="_blank" rel="noopener noreferrer">testa</a> en router med 3 500 procedurer och 1 000 routrar. Men detta testar bara hur långt vi kan pressa TS-kompilatorn innan den bryter samman, och inte hur lång tid typkontrollen tar. Vi testar alla tre delarna av biblioteket (server, vanilla-klient och React-klienten) eftersom de alla har olika kodvägar. Tidigare har vi sett regressioner som är isolerade till en sektion av biblioteket och förlitar oss på våra tester för att visa oss när dessa oväntade beteenden uppstår. (Vi vill fortfarande <a href="https://github.com/trpc/trpc/issues/2892" target="_blank" rel="noopener noreferrer">göra mer</a> för att mäta kompileringstider)</p>
<ul>
<li>
<p>Långsam typkontroll med tsc</p>
</li>
<li>
<p>Lång initial laddningstid</p>
</li>
<li>
<p>Om TypeScript-språkservern tar lång tid på sig att svara på ändringar</p>
</li>
<li>
<p>Den sista punkten är att <code>tsc</code> kan vara långsam vid type-checking, vilket tRPC måste vara extra uppmärksam på. Du vill <strong>aldrig</strong> att dina utvecklare ska behöva vänta på att språkservern ska uppdateras efter en ändring. Det är här tRPC <strong>måste</strong> bibehålla prestanda så att du kan njuta av enastående DX.</p>
</li>
</ul>
<h2 class="anchor anchorWithStickyNavbar_MYIC" id="hur-jag-hittade-prestandamöjligheter-i-trpc">Hur jag hittade prestandamöjligheter i tRPC<a href="https://trpc.io/sv/blog/typescript-performance-lessons#hur-jag-hittade-prestandam%C3%B6jligheter-i-trpc" class="hash-link" aria-label="Direktlänk till Hur jag hittade prestandamöjligheter i tRPC" title="Direktlänk till Hur jag hittade prestandamöjligheter i tRPC">​</a></h2>
<p>Det finns alltid en avvägning mellan TypeScript-accuracy och kompilatorprestanda. Båda är viktiga aspekter för andra utvecklare så vi måste vara extremt medvetna om hur vi skriver typer. Finns det risk för allvarliga fel i en applikation om en viss typ är "för lös"? Är prestandavinsten värd det?</p>
<p>Kommer det ens att bli en märkbar prestandavinst? Bra fråga.</p>
<h2 class="anchor anchorWithStickyNavbar_MYIC" id="hur-jag-hittade-prestandamöjligheter-i-trpc-1">Hur jag hittade prestandamöjligheter i tRPC<a href="https://trpc.io/sv/blog/typescript-performance-lessons#hur-jag-hittade-prestandam%C3%B6jligheter-i-trpc-1" class="hash-link" aria-label="Direktlänk till Hur jag hittade prestandamöjligheter i tRPC" title="Direktlänk till Hur jag hittade prestandamöjligheter i tRPC">​</a></h2>
<p>Det finns alltid en avvägning mellan TypeScripts noggrannhet och kompilatorns prestanda. Båda är viktiga aspekter för andra utvecklare, så vi måste vara mycket medvetna om hur vi skriver typer. Kan en applikation stöta på allvarliga fel på grund av att en viss typ är "för lös"? Är prestandavinsten värt det?</p>
<p>Kommer det ens att bli någon märkbar prestandavinst alls? Bra fråga.</p>
<p>Låt oss titta på <em>hur</em> man hittar tillfällen för prestandaförbättringar i TypeScript-kod. Vi går igenom processen jag följde för att skapa <a href="https://github.com/trpc/trpc/pull/2716" target="_blank" rel="noopener noreferrer">PR #2716</a>, vilket resulterade i 59% kortare TS-kompileringstid.</p>
<hr>
<h2 class="anchor anchorWithStickyNavbar_MYIC" id="slug-typescript-performance-lessonstitle-lärdomar-om-typescript-prestanda-vid-omstrukturering-för-v10authors-sachinraja">slug: typescript-performance-lessons
title: Lärdomar om TypeScript-prestanda vid omstrukturering för v10
authors: [sachinraja]<a href="https://trpc.io/sv/blog/typescript-performance-lessons#slug-typescript-performance-lessonstitle-l%C3%A4rdomar-om-typescript-prestanda-vid-omstrukturering-f%C3%B6r-v10authors-sachinraja" class="hash-link" aria-label="Direktlänk till slug: typescript-performance-lessons
title: Lärdomar om TypeScript-prestanda vid omstrukturering för v10
authors: [sachinraja]" title="Direktlänk till slug: typescript-performance-lessons
title: Lärdomar om TypeScript-prestanda vid omstrukturering för v10
authors: [sachinraja]">​</a></h2>
<p>TypeScript har ett inbyggt <a href="https://github.com/microsoft/TypeScript/wiki/Performance-Tracing" target="_blank" rel="noopener noreferrer">spårningsverktyg</a> som hjälper dig hitta flaskhalsar i dina typer. Det är inte perfekt, men det bästa tillgängliga verktyget.</p>
<p>Det är idealiskt att testa ditt bibliotek i en verklig applikation för att simulera vad ditt bibliotek gör för utvecklare. För tRPC skapade jag en grundläggande <a href="https://create.t3.gg/" target="_blank" rel="noopener noreferrer">T3-app</a> som liknar vad många användare arbetar med.</p>
<p>Så här spårade jag tRPC:</p>
<ol>
<li>
<p><a href="https://docs.npmjs.com/cli/commands/npm-link" target="_blank" rel="noopener noreferrer">Länka biblioteket lokalt</a> till exempelappen. Detta gör att du kan ändra bibliotekskod och omedelbart testa ändringar lokalt.</p>
</li>
<li>
<p>Kör detta kommando i exempelappen:</p>
<div><pre class="shiki min-light" style="background-color: #ffffff; color: #24292eff"><div class="language-id">sh</div><div class="code-container"><code><div class="line"><span style="color: #24292EFF">tsc --generateTrace ./trace --incremental </span><span style="color: #6F42C1">false</span></div></code></div><button type="button" aria-label="Copy code to clipboard" class="copy-button" onclick="navigator.clipboard.writeText(this.previousSibling.innerText),this.classList.add(&quot;copied&quot;),this.textContent=&quot;Copied&quot;,setTimeout(()=>{this.classList.remove(&quot;copied&quot;),this.textContent=&quot;Copy&quot;},2e3)">Copy</button></pre>
<pre class="shiki github-dark" style="background-color: #0d1117; color: #c9d1d9"><div class="language-id">sh</div><div class="code-container"><code><div class="line"><span style="color: #C9D1D9">tsc --generateTrace ./trace --incremental </span><span style="color: #79C0FF">false</span></div></code></div><button type="button" aria-label="Copy code to clipboard" class="copy-button" onclick="navigator.clipboard.writeText(this.previousSibling.innerText),this.classList.add(&quot;copied&quot;),this.textContent=&quot;Copied&quot;,setTimeout(()=>{this.classList.remove(&quot;copied&quot;),this.textContent=&quot;Copy&quot;},2e3)">Copy</button></pre></div>
</li>
<li>
<p>Du får en <code>trace/trace.json</code>-fil. Öppna den i ett spårningsverktyg (jag använder <a href="https://ui.perfetto.dev/" target="_blank" rel="noopener noreferrer">Perfetto</a>) eller <code>chrome://tracing</code>.</p>
</li>
</ol>
<p>Här blir det intressant - vi kan börja analysera typ-prestandan. Så här såg den första spårningen ut:
<img decoding="async" loading="lazy" src="https://assets.trpc.io/www/blog/2023-01-14-typescript-performance-lessons/trace-1.png" alt="Spårningsfält som visar att src/pages/index.ts tog 332ms att typkontrollera" class="img_Njog"></p>
<p>Ett längre fält indikerar längre exekveringstid. Jag har valt det gröna fältet i skärmdumpen, vilket visar att <code>src/pages/index.ts</code> är flaskhalsen. Under <code>Duration</code> ser vi att det tog 332ms - lång tid för typkontroll! Det blå <code>checkVariableDeclaration</code>-fältet visar att kompilatorn ägnade mest tid åt en variabel.
Klickar man på fältet ser vi detaljer:
<img decoding="async" loading="lazy" src="https://assets.trpc.io/www/blog/2023-01-14-typescript-performance-lessons/trace-2.png" alt="Spårningsinfo som visar variabelns position är 275" class="img_Njog">
Fältet <code>pos</code> avslöjar variabelns position i filen. När vi går till den positionen i <code>src/pages/index.ts</code> ser vi att boven är <code>utils = trpc.useContext()</code>!</p>
<p>Men hur är detta möjligt? Vi använder ju bara ett enkelt hook! Låt oss titta på koden:</p>
<div><pre class="shiki min-light" style="background-color: #ffffff; color: #24292eff"><div class="language-id">tsx</div><div class="code-container"><code><div class="line"><span style="color: #D32F2F">import</span><span style="color: #24292EFF"> </span><span style="color: #D32F2F">type</span><span style="color: #24292EFF"> { AppRouter } </span><span style="color: #D32F2F">from</span><span style="color: #24292EFF"> </span><span style="color: #22863A">'~/server/trpc'</span><span style="color: #24292EFF">;</span></div><div class="line"></div><div class="line"><span style="color: #D32F2F">const</span><span style="color: #24292EFF"> </span><span style="color: #1976D2">trpc</span><span style="color: #24292EFF"> </span><span style="color: #D32F2F">=</span><span style="color: #24292EFF"> </span><span style="color: #6F42C1">createTRPCReact</span><span style="color: #24292EFF">&lt;</span><span style="color: #6F42C1">AppRouter</span><span style="color: #24292EFF">&gt;();</span></div><div class="line"><span style="color: #D32F2F">const</span><span style="color: #24292EFF"> </span><span style="color: #6F42C1">Home</span><span style="color: #D32F2F">:</span><span style="color: #24292EFF"> </span><span style="color: #6F42C1">NextPage</span><span style="color: #24292EFF"> </span><span style="color: #D32F2F">=</span><span style="color: #24292EFF"> () </span><span style="color: #D32F2F">=&gt;</span><span style="color: #24292EFF"> {</span></div><div class="line"><span style="color: #24292EFF">  </span><span style="color: #D32F2F">const</span><span style="color: #24292EFF"> { </span><span style="color: #1976D2">data</span><span style="color: #24292EFF"> } </span><span style="color: #D32F2F">=</span><span style="color: #24292EFF"> </span><span style="color: #1976D2">trpc</span><span style="color: #6F42C1">.</span><span style="color: #1976D2">r0</span><span style="color: #6F42C1">.</span><span style="color: #1976D2">greeting</span><span style="color: #6F42C1">.useQuery</span><span style="color: #24292EFF">({ who</span><span style="color: #D32F2F">:</span><span style="color: #24292EFF"> </span><span style="color: #22863A">'from tRPC'</span><span style="color: #24292EFF"> });</span></div><div class="line"></div><div class="line"><span style="color: #24292EFF">  </span><span style="color: #D32F2F">const</span><span style="color: #24292EFF"> </span><span style="color: #1976D2">utils</span><span style="color: #24292EFF"> </span><span style="color: #D32F2F">=</span><span style="color: #24292EFF"> </span><span style="color: #1976D2">trpc</span><span style="color: #6F42C1">.useContext</span><span style="color: #24292EFF">();</span></div><div class="line"></div><div class="line"><span style="color: #24292EFF">  </span><span style="color: #1976D2">utils</span><span style="color: #6F42C1">.</span><span style="color: #1976D2">r49</span><span style="color: #6F42C1">.</span><span style="color: #1976D2">greeting</span><span style="color: #6F42C1">.invalidate</span><span style="color: #24292EFF">();</span></div><div class="line"><span style="color: #24292EFF">};</span></div><div class="line"></div><div class="line"><span style="color: #D32F2F">export</span><span style="color: #24292EFF"> </span><span style="color: #D32F2F">default</span><span style="color: #24292EFF"> Home;</span></div></code></div><button type="button" aria-label="Copy code to clipboard" class="copy-button" onclick="navigator.clipboard.writeText(this.previousSibling.innerText),this.classList.add(&quot;copied&quot;),this.textContent=&quot;Copied&quot;,setTimeout(()=>{this.classList.remove(&quot;copied&quot;),this.textContent=&quot;Copy&quot;},2e3)">Copy</button></pre>
<pre class="shiki github-dark" style="background-color: #0d1117; color: #c9d1d9"><div class="language-id">tsx</div><div class="code-container"><code><div class="line"><span style="color: #FF7B72">import</span><span style="color: #C9D1D9"> </span><span style="color: #FF7B72">type</span><span style="color: #C9D1D9"> { AppRouter } </span><span style="color: #FF7B72">from</span><span style="color: #C9D1D9"> </span><span style="color: #A5D6FF">'~/server/trpc'</span><span style="color: #C9D1D9">;</span></div><div class="line"></div><div class="line"><span style="color: #FF7B72">const</span><span style="color: #C9D1D9"> </span><span style="color: #79C0FF">trpc</span><span style="color: #C9D1D9"> </span><span style="color: #FF7B72">=</span><span style="color: #C9D1D9"> </span><span style="color: #D2A8FF">createTRPCReact</span><span style="color: #C9D1D9">&lt;</span><span style="color: #FFA657">AppRouter</span><span style="color: #C9D1D9">&gt;();</span></div><div class="line"><span style="color: #FF7B72">const</span><span style="color: #C9D1D9"> </span><span style="color: #D2A8FF">Home</span><span style="color: #FF7B72">:</span><span style="color: #C9D1D9"> </span><span style="color: #FFA657">NextPage</span><span style="color: #C9D1D9"> </span><span style="color: #FF7B72">=</span><span style="color: #C9D1D9"> () </span><span style="color: #FF7B72">=&gt;</span><span style="color: #C9D1D9"> {</span></div><div class="line"><span style="color: #C9D1D9">  </span><span style="color: #FF7B72">const</span><span style="color: #C9D1D9"> { </span><span style="color: #79C0FF">data</span><span style="color: #C9D1D9"> } </span><span style="color: #FF7B72">=</span><span style="color: #C9D1D9"> trpc.r0.greeting.</span><span style="color: #D2A8FF">useQuery</span><span style="color: #C9D1D9">({ who: </span><span style="color: #A5D6FF">'from tRPC'</span><span style="color: #C9D1D9"> });</span></div><div class="line"></div><div class="line"><span style="color: #C9D1D9">  </span><span style="color: #FF7B72">const</span><span style="color: #C9D1D9"> </span><span style="color: #79C0FF">utils</span><span style="color: #C9D1D9"> </span><span style="color: #FF7B72">=</span><span style="color: #C9D1D9"> trpc.</span><span style="color: #D2A8FF">useContext</span><span style="color: #C9D1D9">();</span></div><div class="line"></div><div class="line"><span style="color: #C9D1D9">  utils.r49.greeting.</span><span style="color: #D2A8FF">invalidate</span><span style="color: #C9D1D9">();</span></div><div class="line"><span style="color: #C9D1D9">};</span></div><div class="line"></div><div class="line"><span style="color: #FF7B72">export</span><span style="color: #FFA657"> </span><span style="color: #FF7B72">default</span><span style="color: #FFA657"> </span><span style="color: #C9D1D9">Home;</span></div></code></div><button type="button" aria-label="Copy code to clipboard" class="copy-button" onclick="navigator.clipboard.writeText(this.previousSibling.innerText),this.classList.add(&quot;copied&quot;),this.textContent=&quot;Copied&quot;,setTimeout(()=>{this.classList.remove(&quot;copied&quot;),this.textContent=&quot;Copy&quot;},2e3)">Copy</button></pre></div>
<p>Okej, inte mycket att se här. Bara ett <code>useContext</code> och en query-ogiltigförklaring. Inget som <em>borde vara</em> TypeScript-tungt vid första anblick, vilket indikerar att problemet ligger djupare i stacken. Låt oss undersöka typerna bakom denna variabel:</p>
<div><pre class="shiki min-light" style="background-color: #ffffff; color: #24292eff"><div class="language-id">ts</div><div class="code-container"><code><div class="line"><span style="color: #D32F2F">type</span><span style="color: #24292EFF"> </span><span style="color: #6F42C1">DecorateProcedure</span><span style="color: #24292EFF">&lt;</span></div><div class="line"><span style="color: #24292EFF">  </span><span style="color: #6F42C1">TRouter</span><span style="color: #24292EFF"> </span><span style="color: #D32F2F">extends</span><span style="color: #24292EFF"> </span><span style="color: #6F42C1">AnyRouter</span><span style="color: #212121">,</span></div><div class="line"><span style="color: #24292EFF">  </span><span style="color: #6F42C1">TProcedure</span><span style="color: #24292EFF"> </span><span style="color: #D32F2F">extends</span><span style="color: #24292EFF"> </span><span style="color: #6F42C1">Procedure</span><span style="color: #24292EFF">&lt;</span><span style="color: #1976D2">any</span><span style="color: #24292EFF">&gt;</span><span style="color: #212121">,</span></div><div class="line"><span style="color: #24292EFF">  </span><span style="color: #6F42C1">TProcedure</span><span style="color: #24292EFF"> </span><span style="color: #D32F2F">extends</span><span style="color: #24292EFF"> </span><span style="color: #6F42C1">AnyQueryProcedure</span><span style="color: #212121">,</span></div><div class="line"><span style="color: #24292EFF">&gt; </span><span style="color: #D32F2F">=</span><span style="color: #24292EFF"> {</span></div><div class="line"><span style="color: #24292EFF">  </span><span style="color: #C2C3C5">/**</span></div><div class="line"><span style="color: #C2C3C5">   * </span><span style="color: #D32F2F">@see</span><span style="color: #C2C3C5"> https://tanstack.com/query/v4/docs/framework/react/guides/query-invalidation</span></div><div class="line"><span style="color: #C2C3C5">   */</span></div><div class="line"><span style="color: #24292EFF">  </span><span style="color: #6F42C1">invalidate</span><span style="color: #24292EFF">(</span></div><div class="line"><span style="color: #24292EFF">    input</span><span style="color: #D32F2F">?:</span><span style="color: #24292EFF"> </span><span style="color: #6F42C1">inferProcedureInput</span><span style="color: #24292EFF">&lt;</span><span style="color: #6F42C1">TProcedure</span><span style="color: #24292EFF">&gt;</span><span style="color: #212121">,</span></div><div class="line"><span style="color: #24292EFF">    filters</span><span style="color: #D32F2F">?:</span><span style="color: #24292EFF"> </span><span style="color: #6F42C1">InvalidateQueryFilters</span><span style="color: #212121">,</span></div><div class="line"><span style="color: #24292EFF">    options</span><span style="color: #D32F2F">?:</span><span style="color: #24292EFF"> </span><span style="color: #6F42C1">InvalidateOptions</span><span style="color: #212121">,</span></div><div class="line"><span style="color: #24292EFF">  )</span><span style="color: #D32F2F">:</span><span style="color: #24292EFF"> </span><span style="color: #6F42C1">Promise</span><span style="color: #24292EFF">&lt;</span><span style="color: #1976D2">void</span><span style="color: #24292EFF">&gt;;</span></div><div class="line"><span style="color: #24292EFF">  </span><span style="color: #C2C3C5">// ... and so on for all the other React Query utilities</span></div><div class="line"><span style="color: #24292EFF">};</span></div><div class="line"></div><div class="line"><span style="color: #D32F2F">export</span><span style="color: #24292EFF"> </span><span style="color: #D32F2F">type</span><span style="color: #24292EFF"> </span><span style="color: #6F42C1">DecoratedProcedureUtilsRecord</span><span style="color: #24292EFF">&lt;</span><span style="color: #6F42C1">TRouter</span><span style="color: #24292EFF"> </span><span style="color: #D32F2F">extends</span><span style="color: #24292EFF"> </span><span style="color: #6F42C1">AnyRouter</span><span style="color: #24292EFF">&gt; </span><span style="color: #D32F2F">=</span></div><div class="line"><span style="color: #24292EFF">  </span><span style="color: #6F42C1">OmitNeverKeys</span><span style="color: #24292EFF">&lt;{</span></div><div class="line"><span style="color: #24292EFF">    [</span><span style="color: #6F42C1">TKey</span><span style="color: #24292EFF"> </span><span style="color: #D32F2F">in</span><span style="color: #24292EFF"> </span><span style="color: #D32F2F">keyof</span><span style="color: #24292EFF"> </span><span style="color: #6F42C1">TRouter</span><span style="color: #24292EFF">[</span><span style="color: #22863A">'_def'</span><span style="color: #24292EFF">][</span><span style="color: #22863A">'record'</span><span style="color: #24292EFF">]]</span><span style="color: #D32F2F">:</span><span style="color: #24292EFF"> </span><span style="color: #6F42C1">TRouter</span><span style="color: #24292EFF">[</span><span style="color: #22863A">'_def'</span><span style="color: #24292EFF">][</span><span style="color: #22863A">'record'</span><span style="color: #24292EFF">][</span><span style="color: #6F42C1">TKey</span><span style="color: #24292EFF">] </span><span style="color: #D32F2F">extends</span><span style="color: #24292EFF"> </span><span style="color: #6F42C1">LegacyV9ProcedureTag</span></div><div class="line"><span style="color: #24292EFF">      </span><span style="color: #D32F2F">?</span><span style="color: #24292EFF"> </span><span style="color: #1976D2">never</span></div><div class="line"><span style="color: #24292EFF">      </span><span style="color: #D32F2F">:</span><span style="color: #24292EFF"> </span><span style="color: #6F42C1">TRouter</span><span style="color: #24292EFF">[</span><span style="color: #22863A">'_def'</span><span style="color: #24292EFF">][</span><span style="color: #22863A">'record'</span><span style="color: #24292EFF">][</span><span style="color: #6F42C1">TKey</span><span style="color: #24292EFF">] </span><span style="color: #D32F2F">extends</span><span style="color: #24292EFF"> </span><span style="color: #6F42C1">AnyRouter</span></div><div class="line"><span style="color: #24292EFF">        </span><span style="color: #D32F2F">?</span><span style="color: #24292EFF"> </span><span style="color: #6F42C1">DecoratedProcedureUtilsRecord</span><span style="color: #24292EFF">&lt;</span><span style="color: #6F42C1">TRouter</span><span style="color: #24292EFF">[</span><span style="color: #22863A">'_def'</span><span style="color: #24292EFF">][</span><span style="color: #22863A">'record'</span><span style="color: #24292EFF">][</span><span style="color: #6F42C1">TKey</span><span style="color: #24292EFF">]&gt;</span></div><div class="line"><span style="color: #24292EFF">        </span><span style="color: #D32F2F">:</span><span style="color: #24292EFF"> </span><span style="color: #6F42C1">TRouter</span><span style="color: #24292EFF">[</span><span style="color: #22863A">'_def'</span><span style="color: #24292EFF">][</span><span style="color: #22863A">'record'</span><span style="color: #24292EFF">][</span><span style="color: #6F42C1">TKey</span><span style="color: #24292EFF">] </span><span style="color: #D32F2F">extends</span><span style="color: #24292EFF"> </span><span style="color: #6F42C1">AnyQueryProcedure</span></div><div class="line"><span style="color: #24292EFF">          </span><span style="color: #D32F2F">?</span><span style="color: #24292EFF"> </span><span style="color: #6F42C1">DecorateProcedure</span><span style="color: #24292EFF">&lt;</span><span style="color: #6F42C1">TRouter</span><span style="color: #212121">,</span><span style="color: #24292EFF"> </span><span style="color: #6F42C1">TRouter</span><span style="color: #24292EFF">[</span><span style="color: #22863A">'_def'</span><span style="color: #24292EFF">][</span><span style="color: #22863A">'record'</span><span style="color: #24292EFF">][</span><span style="color: #6F42C1">TKey</span><span style="color: #24292EFF">]&gt;</span></div><div class="line"><span style="color: #24292EFF">          </span><span style="color: #D32F2F">:</span><span style="color: #24292EFF"> </span><span style="color: #1976D2">never</span><span style="color: #24292EFF">;</span></div><div class="line"><span style="color: #24292EFF">  }&gt;;</span></div></code></div><button type="button" aria-label="Copy code to clipboard" class="copy-button" onclick="navigator.clipboard.writeText(this.previousSibling.innerText),this.classList.add(&quot;copied&quot;),this.textContent=&quot;Copied&quot;,setTimeout(()=>{this.classList.remove(&quot;copied&quot;),this.textContent=&quot;Copy&quot;},2e3)">Copy</button></pre>
<pre class="shiki github-dark" style="background-color: #0d1117; color: #c9d1d9"><div class="language-id">ts</div><div class="code-container"><code><div class="line"><span style="color: #FF7B72">type</span><span style="color: #C9D1D9"> </span><span style="color: #FFA657">DecorateProcedure</span><span style="color: #C9D1D9">&lt;</span></div><div class="line"><span style="color: #C9D1D9">  </span><span style="color: #FFA657">TRouter</span><span style="color: #C9D1D9"> </span><span style="color: #FF7B72">extends</span><span style="color: #C9D1D9"> </span><span style="color: #FFA657">AnyRouter</span><span style="color: #C9D1D9">,</span></div><div class="line"><span style="color: #C9D1D9">  </span><span style="color: #FFA657">TProcedure</span><span style="color: #C9D1D9"> </span><span style="color: #FF7B72">extends</span><span style="color: #C9D1D9"> </span><span style="color: #FFA657">Procedure</span><span style="color: #C9D1D9">&lt;</span><span style="color: #79C0FF">any</span><span style="color: #C9D1D9">&gt;,</span></div><div class="line"><span style="color: #C9D1D9">  </span><span style="color: #FFA657">TProcedure</span><span style="color: #C9D1D9"> </span><span style="color: #FF7B72">extends</span><span style="color: #C9D1D9"> </span><span style="color: #FFA657">AnyQueryProcedure</span><span style="color: #C9D1D9">,</span></div><div class="line"><span style="color: #C9D1D9">&gt; </span><span style="color: #FF7B72">=</span><span style="color: #C9D1D9"> {</span></div><div class="line"><span style="color: #C9D1D9">  </span><span style="color: #8B949E">/**</span></div><div class="line"><span style="color: #8B949E">   * </span><span style="color: #FF7B72">@see</span><span style="color: #8B949E"> </span><span style="color: #C9D1D9">https://tanstack.com/query/v4/docs/framework/react/guides/query-invalidation</span></div><div class="line"><span style="color: #8B949E">   */</span></div><div class="line"><span style="color: #C9D1D9">  </span><span style="color: #D2A8FF">invalidate</span><span style="color: #C9D1D9">(</span></div><div class="line"><span style="color: #C9D1D9">    </span><span style="color: #FFA657">input</span><span style="color: #FF7B72">?:</span><span style="color: #C9D1D9"> </span><span style="color: #FFA657">inferProcedureInput</span><span style="color: #C9D1D9">&lt;</span><span style="color: #FFA657">TProcedure</span><span style="color: #C9D1D9">&gt;,</span></div><div class="line"><span style="color: #C9D1D9">    </span><span style="color: #FFA657">filters</span><span style="color: #FF7B72">?:</span><span style="color: #C9D1D9"> </span><span style="color: #FFA657">InvalidateQueryFilters</span><span style="color: #C9D1D9">,</span></div><div class="line"><span style="color: #C9D1D9">    </span><span style="color: #FFA657">options</span><span style="color: #FF7B72">?:</span><span style="color: #C9D1D9"> </span><span style="color: #FFA657">InvalidateOptions</span><span style="color: #C9D1D9">,</span></div><div class="line"><span style="color: #C9D1D9">  )</span><span style="color: #FF7B72">:</span><span style="color: #C9D1D9"> </span><span style="color: #FFA657">Promise</span><span style="color: #C9D1D9">&lt;</span><span style="color: #79C0FF">void</span><span style="color: #C9D1D9">&gt;;</span></div><div class="line"><span style="color: #C9D1D9">  </span><span style="color: #8B949E">// ... and so on for all the other React Query utilities</span></div><div class="line"><span style="color: #C9D1D9">};</span></div><div class="line"></div><div class="line"><span style="color: #FF7B72">export</span><span style="color: #C9D1D9"> </span><span style="color: #FF7B72">type</span><span style="color: #C9D1D9"> </span><span style="color: #FFA657">DecoratedProcedureUtilsRecord</span><span style="color: #C9D1D9">&lt;</span><span style="color: #FFA657">TRouter</span><span style="color: #C9D1D9"> </span><span style="color: #FF7B72">extends</span><span style="color: #C9D1D9"> </span><span style="color: #FFA657">AnyRouter</span><span style="color: #C9D1D9">&gt; </span><span style="color: #FF7B72">=</span></div><div class="line"><span style="color: #C9D1D9">  </span><span style="color: #FFA657">OmitNeverKeys</span><span style="color: #C9D1D9">&lt;{</span></div><div class="line"><span style="color: #C9D1D9">    [</span><span style="color: #FFA657">TKey</span><span style="color: #C9D1D9"> </span><span style="color: #FF7B72">in</span><span style="color: #C9D1D9"> </span><span style="color: #FF7B72">keyof</span><span style="color: #C9D1D9"> </span><span style="color: #FFA657">TRouter</span><span style="color: #C9D1D9">[</span><span style="color: #A5D6FF">'_def'</span><span style="color: #C9D1D9">][</span><span style="color: #A5D6FF">'record'</span><span style="color: #C9D1D9">]]</span><span style="color: #FF7B72">:</span><span style="color: #C9D1D9"> </span><span style="color: #FFA657">TRouter</span><span style="color: #C9D1D9">[</span><span style="color: #A5D6FF">'_def'</span><span style="color: #C9D1D9">][</span><span style="color: #A5D6FF">'record'</span><span style="color: #C9D1D9">][</span><span style="color: #FFA657">TKey</span><span style="color: #C9D1D9">] </span><span style="color: #FF7B72">extends</span><span style="color: #C9D1D9"> </span><span style="color: #FFA657">LegacyV9ProcedureTag</span></div><div class="line"><span style="color: #C9D1D9">      </span><span style="color: #FF7B72">?</span><span style="color: #C9D1D9"> </span><span style="color: #79C0FF">never</span></div><div class="line"><span style="color: #C9D1D9">      </span><span style="color: #FF7B72">:</span><span style="color: #C9D1D9"> </span><span style="color: #FFA657">TRouter</span><span style="color: #C9D1D9">[</span><span style="color: #A5D6FF">'_def'</span><span style="color: #C9D1D9">][</span><span style="color: #A5D6FF">'record'</span><span style="color: #C9D1D9">][</span><span style="color: #FFA657">TKey</span><span style="color: #C9D1D9">] </span><span style="color: #FF7B72">extends</span><span style="color: #C9D1D9"> </span><span style="color: #FFA657">AnyRouter</span></div><div class="line"><span style="color: #C9D1D9">        </span><span style="color: #FF7B72">?</span><span style="color: #C9D1D9"> </span><span style="color: #FFA657">DecoratedProcedureUtilsRecord</span><span style="color: #C9D1D9">&lt;</span><span style="color: #FFA657">TRouter</span><span style="color: #C9D1D9">[</span><span style="color: #A5D6FF">'_def'</span><span style="color: #C9D1D9">][</span><span style="color: #A5D6FF">'record'</span><span style="color: #C9D1D9">][</span><span style="color: #FFA657">TKey</span><span style="color: #C9D1D9">]&gt;</span></div><div class="line"><span style="color: #C9D1D9">        </span><span style="color: #FF7B72">:</span><span style="color: #C9D1D9"> </span><span style="color: #FFA657">TRouter</span><span style="color: #C9D1D9">[</span><span style="color: #A5D6FF">'_def'</span><span style="color: #C9D1D9">][</span><span style="color: #A5D6FF">'record'</span><span style="color: #C9D1D9">][</span><span style="color: #FFA657">TKey</span><span style="color: #C9D1D9">] </span><span style="color: #FF7B72">extends</span><span style="color: #C9D1D9"> </span><span style="color: #FFA657">AnyQueryProcedure</span></div><div class="line"><span style="color: #C9D1D9">          </span><span style="color: #FF7B72">?</span><span style="color: #C9D1D9"> </span><span style="color: #FFA657">DecorateProcedure</span><span style="color: #C9D1D9">&lt;</span><span style="color: #FFA657">TRouter</span><span style="color: #C9D1D9">, </span><span style="color: #FFA657">TRouter</span><span style="color: #C9D1D9">[</span><span style="color: #A5D6FF">'_def'</span><span style="color: #C9D1D9">][</span><span style="color: #A5D6FF">'record'</span><span style="color: #C9D1D9">][</span><span style="color: #FFA657">TKey</span><span style="color: #C9D1D9">]&gt;</span></div><div class="line"><span style="color: #C9D1D9">          </span><span style="color: #FF7B72">:</span><span style="color: #C9D1D9"> </span><span style="color: #79C0FF">never</span><span style="color: #C9D1D9">;</span></div><div class="line"><span style="color: #C9D1D9">  }&gt;;</span></div></code></div><button type="button" aria-label="Copy code to clipboard" class="copy-button" onclick="navigator.clipboard.writeText(this.previousSibling.innerText),this.classList.add(&quot;copied&quot;),this.textContent=&quot;Copied&quot;,setTimeout(()=>{this.classList.remove(&quot;copied&quot;),this.textContent=&quot;Copy&quot;},2e3)">Copy</button></pre></div>
<p>Nu har vi saker att analysera. Låt oss först förstå vad denna kod gör.</p>
<p>Vi har en rekursiv typ <code>DecoratedProcedureUtilsRecord</code> som traverserar alla procedurer i routern och "dekorerar" (lägger till metoder på) dem med React Query-verktyg som <a href="https://tanstack.com/query/v4/docs/framework/react/guides/query-invalidation" target="_blank" rel="noopener noreferrer"><code>invalidateQueries</code></a>.</p>
<p>I tRPC v10 stöder vi fortfarande gamla <code>v9</code>-routrar, men <code>v10</code>-klienter kan inte anropa procedurer från <code>v9</code>-routrar. Så för varje procedur kontrollerar vi om det är en <code>v9</code>-procedur (<code>extends LegacyV9ProcedureTag</code>) och filtrerar bort den i så fall. Detta är mycket arbete för TypeScript...<strong>om det inte utvärderas lättjefullt</strong>.</p>
<h3 class="anchor anchorWithStickyNavbar_MYIC" id="lat-utvärdering">Lat utvärdering<a href="https://trpc.io/sv/blog/typescript-performance-lessons#lat-utv%C3%A4rdering" class="hash-link" aria-label="Direktlänk till Lat utvärdering" title="Direktlänk till Lat utvärdering">​</a></h3>
<p>Problemet här är att TypeScript utvärderar all denna kod i typsystemet, även om den inte används omedelbart. Vår kod använder bara <code>utils.r49.greeting.invalidate</code>, så TypeScript borde bara behöva packa upp <code>r49</code>-egenskapen (en router), sedan <code>greeting</code>-egenskapen (en procedur), och slutligen <code>invalidate</code>-funktionen för den proceduren. Inga andra typer behövs i den koden och att omedelbart hitta typen för varje React Query-hjälpmetod för alla dina tRPC-procedurer skulle onödigt sakta ner TypeScript. TypeScript skjuter upp typutvärderingen av egenskaper på <strong>objekt</strong> tills de används direkt, så teoretiskt borde vår typ ovan få lazy evaluation... eller hur?</p>
<p>Nåväl, det är inte <em>exakt</em> ett objekt. Det finns faktiskt en typ som omsluter hela grejen: <code>OmitNeverKeys</code>. Denna typ är ett verktyg som tar bort nycklar med värdet <code>never</code> från ett objekt. Det är här vi skrapar bort v9-procedurerna så dessa egenskaper inte syns i Intellisense.</p>
<p>Men detta skapar ett enormt prestandaproblem. Vi tvingade TypeScript att utvärdera värdena för <strong>alla</strong> typer nu för att kontrollera om de är <code>never</code>.</p>
<p>Hur fixar vi detta? Låt oss ändra våra typer till att <em>göra mindre</em>.</p>
<h3 class="anchor anchorWithStickyNavbar_MYIC" id="bli-lat">Bli lat<a href="https://trpc.io/sv/blog/typescript-performance-lessons#bli-lat" class="hash-link" aria-label="Direktlänk till Bli lat" title="Direktlänk till Bli lat">​</a></h3>
<p>Vi måste hitta ett sätt för <code>v10</code>-API<!-- -->:et<!-- --> att anpassa sig till de äldre <code>v9</code>-routern mer graciöst. Nya tRPC-projekt borde inte drabbas av den reducerade TypeScript-prestandan i <a href="https://trpc.io/sv/docs/v10/migrate-from-v9-to-v10#using-interop">interop-läge</a>.</p>
<p>Tanken är att omstrukturera kärntyperna själva. <code>v9</code>-procedurer är olika entiteter jämfört med <code>v10</code>-procedurer så de borde inte dela samma utrymme i vår bibliotekskod. På tRPC-serversidan innebar detta att vi hade arbete att göra för att lagra typerna på olika fält i routern istället för ett enda <code>record</code>-fält (se <code>DecoratedProcedureUtilsRecord</code> ovan).</p>
<p>Vi gjorde en ändring så att <code>v9</code>-routrar injicerar sina procedurer i ett <code>legacy</code>-fält när de konverteras till <code>v10</code>-routrar.</p>
<p>Gamla typer:</p>
<div><pre class="shiki min-light" style="background-color: #ffffff; color: #24292eff"><div class="language-id">ts</div><div class="code-container"><code><div class="line"><span style="color: #D32F2F">export</span><span style="color: #24292EFF"> </span><span style="color: #D32F2F">type</span><span style="color: #24292EFF"> </span><span style="color: #6F42C1">V10Router</span><span style="color: #24292EFF">&lt;</span><span style="color: #6F42C1">TProcedureRecord</span><span style="color: #24292EFF">&gt; </span><span style="color: #D32F2F">=</span><span style="color: #24292EFF"> {</span></div><div class="line"><span style="color: #24292EFF">  record</span><span style="color: #D32F2F">:</span><span style="color: #24292EFF"> </span><span style="color: #6F42C1">TProcedureRecord</span><span style="color: #24292EFF">;</span></div><div class="line"><span style="color: #24292EFF">};</span></div><div class="line"></div><div class="line"><span style="color: #C2C3C5">// convert a v9 interop router to a v10 router</span></div><div class="line"><span style="color: #D32F2F">export</span><span style="color: #24292EFF"> </span><span style="color: #D32F2F">type</span><span style="color: #24292EFF"> </span><span style="color: #6F42C1">MigrateV9Router</span><span style="color: #24292EFF">&lt;</span><span style="color: #6F42C1">TV9Router</span><span style="color: #24292EFF"> </span><span style="color: #D32F2F">extends</span><span style="color: #24292EFF"> </span><span style="color: #6F42C1">V9Router</span><span style="color: #24292EFF">&gt; </span><span style="color: #D32F2F">=</span><span style="color: #24292EFF"> </span><span style="color: #6F42C1">V10Router</span><span style="color: #24292EFF">&lt;{</span></div><div class="line"><span style="color: #24292EFF">  [</span><span style="color: #6F42C1">TKey</span><span style="color: #24292EFF"> </span><span style="color: #D32F2F">in</span><span style="color: #24292EFF"> </span><span style="color: #D32F2F">keyof</span><span style="color: #24292EFF"> </span><span style="color: #6F42C1">TV9Router</span><span style="color: #24292EFF">[</span><span style="color: #22863A">'procedures'</span><span style="color: #24292EFF">]]</span><span style="color: #D32F2F">:</span><span style="color: #24292EFF"> </span><span style="color: #6F42C1">MigrateProcedure</span><span style="color: #24292EFF">&lt;</span></div><div class="line"><span style="color: #24292EFF">    </span><span style="color: #6F42C1">TV9Router</span><span style="color: #24292EFF">[</span><span style="color: #22863A">'procedures'</span><span style="color: #24292EFF">][</span><span style="color: #6F42C1">TKey</span><span style="color: #24292EFF">]</span></div><div class="line"><span style="color: #24292EFF">  &gt; </span><span style="color: #D32F2F">&amp;</span></div><div class="line"><span style="color: #24292EFF">    </span><span style="color: #6F42C1">LegacyV9ProcedureTag</span><span style="color: #24292EFF">;</span></div><div class="line"><span style="color: #24292EFF">}&gt;;</span></div></code></div><button type="button" aria-label="Copy code to clipboard" class="copy-button" onclick="navigator.clipboard.writeText(this.previousSibling.innerText),this.classList.add(&quot;copied&quot;),this.textContent=&quot;Copied&quot;,setTimeout(()=>{this.classList.remove(&quot;copied&quot;),this.textContent=&quot;Copy&quot;},2e3)">Copy</button></pre>
<pre class="shiki github-dark" style="background-color: #0d1117; color: #c9d1d9"><div class="language-id">ts</div><div class="code-container"><code><div class="line"><span style="color: #FF7B72">export</span><span style="color: #C9D1D9"> </span><span style="color: #FF7B72">type</span><span style="color: #C9D1D9"> </span><span style="color: #FFA657">V10Router</span><span style="color: #C9D1D9">&lt;</span><span style="color: #FFA657">TProcedureRecord</span><span style="color: #C9D1D9">&gt; </span><span style="color: #FF7B72">=</span><span style="color: #C9D1D9"> {</span></div><div class="line"><span style="color: #C9D1D9">  </span><span style="color: #FFA657">record</span><span style="color: #FF7B72">:</span><span style="color: #C9D1D9"> </span><span style="color: #FFA657">TProcedureRecord</span><span style="color: #C9D1D9">;</span></div><div class="line"><span style="color: #C9D1D9">};</span></div><div class="line"></div><div class="line"><span style="color: #8B949E">// convert a v9 interop router to a v10 router</span></div><div class="line"><span style="color: #FF7B72">export</span><span style="color: #C9D1D9"> </span><span style="color: #FF7B72">type</span><span style="color: #C9D1D9"> </span><span style="color: #FFA657">MigrateV9Router</span><span style="color: #C9D1D9">&lt;</span><span style="color: #FFA657">TV9Router</span><span style="color: #C9D1D9"> </span><span style="color: #FF7B72">extends</span><span style="color: #C9D1D9"> </span><span style="color: #FFA657">V9Router</span><span style="color: #C9D1D9">&gt; </span><span style="color: #FF7B72">=</span><span style="color: #C9D1D9"> </span><span style="color: #FFA657">V10Router</span><span style="color: #C9D1D9">&lt;{</span></div><div class="line"><span style="color: #C9D1D9">  [</span><span style="color: #FFA657">TKey</span><span style="color: #C9D1D9"> </span><span style="color: #FF7B72">in</span><span style="color: #C9D1D9"> </span><span style="color: #FF7B72">keyof</span><span style="color: #C9D1D9"> </span><span style="color: #FFA657">TV9Router</span><span style="color: #C9D1D9">[</span><span style="color: #A5D6FF">'procedures'</span><span style="color: #C9D1D9">]]</span><span style="color: #FF7B72">:</span><span style="color: #C9D1D9"> </span><span style="color: #FFA657">MigrateProcedure</span><span style="color: #C9D1D9">&lt;</span></div><div class="line"><span style="color: #C9D1D9">    </span><span style="color: #FFA657">TV9Router</span><span style="color: #C9D1D9">[</span><span style="color: #A5D6FF">'procedures'</span><span style="color: #C9D1D9">][</span><span style="color: #FFA657">TKey</span><span style="color: #C9D1D9">]</span></div><div class="line"><span style="color: #C9D1D9">  &gt; </span><span style="color: #FF7B72">&amp;</span></div><div class="line"><span style="color: #C9D1D9">    </span><span style="color: #FFA657">LegacyV9ProcedureTag</span><span style="color: #C9D1D9">;</span></div><div class="line"><span style="color: #C9D1D9">}&gt;;</span></div></code></div><button type="button" aria-label="Copy code to clipboard" class="copy-button" onclick="navigator.clipboard.writeText(this.previousSibling.innerText),this.classList.add(&quot;copied&quot;),this.textContent=&quot;Copied&quot;,setTimeout(()=>{this.classList.remove(&quot;copied&quot;),this.textContent=&quot;Copy&quot;},2e3)">Copy</button></pre></div>
<p>Om du minns <code>DecoratedProcedureUtilsRecord</code>-typen ovan ser du att vi bifogade <code>LegacyV9ProcedureTag</code> här för att skilja på <code>v9</code>- och <code>v10</code>-procedurer på typnivå och framtvinga att <code>v9</code>-procedurer inte anropas från <code>v10</code>-klienter.</p>
<p>Nya typer:</p>
<div><pre class="shiki min-light" style="background-color: #ffffff; color: #24292eff"><div class="language-id">ts</div><div class="code-container"><code><div class="line"><span style="color: #D32F2F">export</span><span style="color: #24292EFF"> </span><span style="color: #D32F2F">type</span><span style="color: #24292EFF"> </span><span style="color: #6F42C1">V10Router</span><span style="color: #24292EFF">&lt;</span><span style="color: #6F42C1">TProcedureRecord</span><span style="color: #24292EFF">&gt; </span><span style="color: #D32F2F">=</span><span style="color: #24292EFF"> {</span></div><div class="line"><span style="color: #24292EFF">  record</span><span style="color: #D32F2F">:</span><span style="color: #24292EFF"> </span><span style="color: #6F42C1">TProcedureRecord</span><span style="color: #24292EFF">;</span></div><div class="line"><span style="color: #24292EFF">  </span><span style="color: #C2C3C5">// by default, no legacy procedures</span></div><div class="line"><span style="color: #24292EFF">  legacy</span><span style="color: #D32F2F">:</span><span style="color: #24292EFF"> {};</span></div><div class="line"><span style="color: #24292EFF">};</span></div><div class="line"></div><div class="line"><span style="color: #D32F2F">export</span><span style="color: #24292EFF"> </span><span style="color: #D32F2F">type</span><span style="color: #24292EFF"> </span><span style="color: #6F42C1">MigrateV9Router</span><span style="color: #24292EFF">&lt;</span><span style="color: #6F42C1">TV9Router</span><span style="color: #24292EFF"> </span><span style="color: #D32F2F">extends</span><span style="color: #24292EFF"> </span><span style="color: #6F42C1">V9Router</span><span style="color: #24292EFF">&gt; </span><span style="color: #D32F2F">=</span><span style="color: #24292EFF"> {</span></div><div class="line"><span style="color: #24292EFF">  </span><span style="color: #C2C3C5">// v9 routers inject their procedures into a `legacy` field</span></div><div class="line"><span style="color: #24292EFF">  legacy</span><span style="color: #D32F2F">:</span><span style="color: #24292EFF"> {</span></div><div class="line"><span style="color: #24292EFF">    </span><span style="color: #C2C3C5">// v9 clients require that we filter queries, mutations, subscriptions at the top-level</span></div><div class="line"><span style="color: #24292EFF">    queries</span><span style="color: #D32F2F">:</span><span style="color: #24292EFF"> </span><span style="color: #6F42C1">MigrateProcedureRecord</span><span style="color: #24292EFF">&lt;</span><span style="color: #6F42C1">TV9Router</span><span style="color: #24292EFF">[</span><span style="color: #22863A">'queries'</span><span style="color: #24292EFF">]&gt;;</span></div><div class="line"><span style="color: #24292EFF">    mutations</span><span style="color: #D32F2F">:</span><span style="color: #24292EFF"> </span><span style="color: #6F42C1">MigrateProcedureRecord</span><span style="color: #24292EFF">&lt;</span><span style="color: #6F42C1">TV9Router</span><span style="color: #24292EFF">[</span><span style="color: #22863A">'mutations'</span><span style="color: #24292EFF">]&gt;;</span></div><div class="line"><span style="color: #24292EFF">    subscriptions</span><span style="color: #D32F2F">:</span><span style="color: #24292EFF"> </span><span style="color: #6F42C1">MigrateProcedureRecord</span><span style="color: #24292EFF">&lt;</span><span style="color: #6F42C1">TV9Router</span><span style="color: #24292EFF">[</span><span style="color: #22863A">'subscriptions'</span><span style="color: #24292EFF">]&gt;;</span></div><div class="line"><span style="color: #24292EFF">  };</span></div><div class="line"><span style="color: #24292EFF">} </span><span style="color: #D32F2F">&amp;</span><span style="color: #24292EFF"> </span><span style="color: #6F42C1">V10Router</span><span style="color: #24292EFF">&lt;</span><span style="color: #C2C3C5">/* empty object, v9 routers have no v10 procedures to pass */</span><span style="color: #24292EFF"> {}&gt;;</span></div></code></div><button type="button" aria-label="Copy code to clipboard" class="copy-button" onclick="navigator.clipboard.writeText(this.previousSibling.innerText),this.classList.add(&quot;copied&quot;),this.textContent=&quot;Copied&quot;,setTimeout(()=>{this.classList.remove(&quot;copied&quot;),this.textContent=&quot;Copy&quot;},2e3)">Copy</button></pre>
<pre class="shiki github-dark" style="background-color: #0d1117; color: #c9d1d9"><div class="language-id">ts</div><div class="code-container"><code><div class="line"><span style="color: #FF7B72">export</span><span style="color: #C9D1D9"> </span><span style="color: #FF7B72">type</span><span style="color: #C9D1D9"> </span><span style="color: #FFA657">V10Router</span><span style="color: #C9D1D9">&lt;</span><span style="color: #FFA657">TProcedureRecord</span><span style="color: #C9D1D9">&gt; </span><span style="color: #FF7B72">=</span><span style="color: #C9D1D9"> {</span></div><div class="line"><span style="color: #C9D1D9">  </span><span style="color: #FFA657">record</span><span style="color: #FF7B72">:</span><span style="color: #C9D1D9"> </span><span style="color: #FFA657">TProcedureRecord</span><span style="color: #C9D1D9">;</span></div><div class="line"><span style="color: #C9D1D9">  </span><span style="color: #8B949E">// by default, no legacy procedures</span></div><div class="line"><span style="color: #C9D1D9">  </span><span style="color: #FFA657">legacy</span><span style="color: #FF7B72">:</span><span style="color: #C9D1D9"> {};</span></div><div class="line"><span style="color: #C9D1D9">};</span></div><div class="line"></div><div class="line"><span style="color: #FF7B72">export</span><span style="color: #C9D1D9"> </span><span style="color: #FF7B72">type</span><span style="color: #C9D1D9"> </span><span style="color: #FFA657">MigrateV9Router</span><span style="color: #C9D1D9">&lt;</span><span style="color: #FFA657">TV9Router</span><span style="color: #C9D1D9"> </span><span style="color: #FF7B72">extends</span><span style="color: #C9D1D9"> </span><span style="color: #FFA657">V9Router</span><span style="color: #C9D1D9">&gt; </span><span style="color: #FF7B72">=</span><span style="color: #C9D1D9"> {</span></div><div class="line"><span style="color: #C9D1D9">  </span><span style="color: #8B949E">// v9 routers inject their procedures into a `legacy` field</span></div><div class="line"><span style="color: #C9D1D9">  </span><span style="color: #FFA657">legacy</span><span style="color: #FF7B72">:</span><span style="color: #C9D1D9"> {</span></div><div class="line"><span style="color: #C9D1D9">    </span><span style="color: #8B949E">// v9 clients require that we filter queries, mutations, subscriptions at the top-level</span></div><div class="line"><span style="color: #C9D1D9">    </span><span style="color: #FFA657">queries</span><span style="color: #FF7B72">:</span><span style="color: #C9D1D9"> </span><span style="color: #FFA657">MigrateProcedureRecord</span><span style="color: #C9D1D9">&lt;</span><span style="color: #FFA657">TV9Router</span><span style="color: #C9D1D9">[</span><span style="color: #A5D6FF">'queries'</span><span style="color: #C9D1D9">]&gt;;</span></div><div class="line"><span style="color: #C9D1D9">    </span><span style="color: #FFA657">mutations</span><span style="color: #FF7B72">:</span><span style="color: #C9D1D9"> </span><span style="color: #FFA657">MigrateProcedureRecord</span><span style="color: #C9D1D9">&lt;</span><span style="color: #FFA657">TV9Router</span><span style="color: #C9D1D9">[</span><span style="color: #A5D6FF">'mutations'</span><span style="color: #C9D1D9">]&gt;;</span></div><div class="line"><span style="color: #C9D1D9">    </span><span style="color: #FFA657">subscriptions</span><span style="color: #FF7B72">:</span><span style="color: #C9D1D9"> </span><span style="color: #FFA657">MigrateProcedureRecord</span><span style="color: #C9D1D9">&lt;</span><span style="color: #FFA657">TV9Router</span><span style="color: #C9D1D9">[</span><span style="color: #A5D6FF">'subscriptions'</span><span style="color: #C9D1D9">]&gt;;</span></div><div class="line"><span style="color: #C9D1D9">  };</span></div><div class="line"><span style="color: #C9D1D9">} </span><span style="color: #FF7B72">&amp;</span><span style="color: #C9D1D9"> </span><span style="color: #FFA657">V10Router</span><span style="color: #C9D1D9">&lt;</span><span style="color: #8B949E">/* empty object, v9 routers have no v10 procedures to pass */</span><span style="color: #C9D1D9"> {}&gt;;</span></div></code></div><button type="button" aria-label="Copy code to clipboard" class="copy-button" onclick="navigator.clipboard.writeText(this.previousSibling.innerText),this.classList.add(&quot;copied&quot;),this.textContent=&quot;Copied&quot;,setTimeout(()=>{this.classList.remove(&quot;copied&quot;),this.textContent=&quot;Copy&quot;},2e3)">Copy</button></pre></div>
<p>Nu kan vi ta bort <code>OmitNeverKeys</code> eftersom procedurerna är försorterade så en routers <code>record</code>-egenskapstyp kommer innehålla alla <code>v10</code>-procedurer och dess <code>legacy</code>-egenskapstyp kommer innehålla alla <code>v9</code>-procedurer. Vi tvingar inte längre TypeScript att helt utvärdera den enorma <code>DecoratedProcedureUtilsRecord</code>-typen. Vi kan också ta bort filtreringen för <code>v9</code>-procedurer med <code>LegacyV9ProcedureTag</code>.</p>
<h2 class="anchor anchorWithStickyNavbar_MYIC" id="funkade-det">Funkade det?<a href="https://trpc.io/sv/blog/typescript-performance-lessons#funkade-det" class="hash-link" aria-label="Direktlänk till Funkade det?" title="Direktlänk till Funkade det?">​</a></h2>
<p>Vår nya spårning visar att flaskhalsen är borta:
<img decoding="async" loading="lazy" src="https://assets.trpc.io/www/blog/2023-01-14-typescript-performance-lessons/trace-3.png" alt="spårningsfält som visar att src/pages/index.ts tog 136ms att typkontrollera" class="img_Njog"></p>
<p>En betydande förbättring! Typkontrolleringstiden gick från 332ms till 136ms 🤯! Det kanske inte verkar mycket i det stora hela men det är en enorm vinst. 200ms är en liten mängd en gång - men tänk på:</p>
<ul>
<li>
<p>hur många andra TS-bibliotek som finns i ett projekt</p>
</li>
<li>
<p>hur många utvecklare som använder tRPC idag</p>
</li>
<li>
<p>hur många gånger deras typer omvärderas under ett arbetspass</p>
</li>
</ul>
<p>Det blir många 200ms som lägger upp till ett väldigt stort antal.</p>
<p>Vi letar alltid efter fler möjligheter att förbättra upplevelsen för TypeScript-utvecklare, vare sig det är med tRPC eller ett TS-baserat problem att lösa i ett annat projekt. Pinga mig på <a href="https://twitter.com/s4chinraja" target="_blank" rel="noopener noreferrer">Twitter</a> om du vill prata TypeScript.</p>
<p>Tack till <a href="https://twitter.com/anthonysheww" target="_blank" rel="noopener noreferrer">Anthony Shew</a> för hjälpen med att skriva detta inlägg och till <a href="https://twitter.com/alexdotjs" target="_blank" rel="noopener noreferrer">Alex</a> för granskningen!</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Vi presenterar tRPC v10]]></title>
            <link>https://trpc.io/sv/blog/announcing-trpc-10</link>
            <guid>https://trpc.io/sv/blog/announcing-trpc-10</guid>
            <pubDate>Mon, 21 Nov 2022 00:00:00 GMT</pubDate>
            <description><![CDATA[Denna sida har översatts av PageTurner AI (beta). Inte officiellt godkänd av projektet.]]></description>
            <content:encoded><![CDATA[<div class="theme-admonition theme-admonition-info admonition_v5Ag alert alert--info"><div class="admonitionHeading_usrK"><span class="admonitionIcon_bgEp"><svg viewBox="0 0 14 16"><path fill-rule="evenodd" d="M7 2.3c3.14 0 5.7 2.56 5.7 5.7s-2.56 5.7-5.7 5.7A5.71 5.71 0 0 1 1.3 8c0-3.14 2.56-5.7 5.7-5.7zM7 1C3.14 1 0 4.14 0 8s3.14 7 7 7 7-3.14 7-7-3.14-7-7-7zm1 3H6v5h2V4zm0 6H6v2h2v-2z"></path></svg></span>Inofficiell Beta-översättning</div><div class="admonitionContent_e2NW"><p>Denna sida har översatts av <a href="https://page-turner.com/" target="_blank" rel="noopener noreferrer"><strong>PageTurner</strong></a> AI (beta). Inte officiellt godkänd av projektet.
Hittade du ett fel? <a href="https://feedback.page-turner.com/?repo_id=683d130a-1828-4b22-91cd-ef2d269ef3f5&amp;file_path=blog%2F2022-11-21-announcing-trpc-10.mdx&amp;locale=sv" target="_blank" rel="noopener noreferrer">Rapportera problem →</a></p></div></div>
<!-- -->
<p>tRPC erbjuder en fantastisk utvecklarupplevelse genom att med TypeScripts kraft skapa tätt sammanlänkade typer i hela stacken. Inga avvikande API-kontrakt, ingen kodgenerering.</p>
<!-- -->
<p>Sedan vårt senaste majorversionssläpp i augusti 2021 har tRPC-communityn vuxit avsevärt:</p>
<ul>
<li>
<p>Vi har nu över <a href="https://github.com/trpc/trpc" target="_blank" rel="noopener noreferrer">15 000 stjärnor på GitHub</a></p>
</li>
<li>
<p><a href="https://trpc.io/discord" target="_blank" rel="noopener noreferrer">En Discord-gemenskap</a> med över 2 000 medlemmar</p>
</li>
<li>
<p><a href="https://www.npmjs.com/package/@trpc/server" target="_blank" rel="noopener noreferrer">100k+ nedladdningar per vecka på npm</a></p>
</li>
<li>
<p><a href="https://github.com/trpc/trpc/graphs/contributors" target="_blank" rel="noopener noreferrer">Nästan 200 bidragsgivare</a></p>
</li>
<li>
<p><a href="https://trpc.io/awesome" target="_blank" rel="noopener noreferrer">Ett växande ekosystem av tillägg, exempel och innehåll</a></p>
</li>
</ul>
<p><strong>Idag lanserar vi tRPC v10</strong>. Vi är glada att dela med oss att v10 redan används i produktion av många stora TypeScript-projekt. Detta officiella släpp markerar allmän tillgänglighet för hela communityn.</p>
<p>För nya projekt kan du komma igång med ett <a href="https://trpc.io/awesome#-starting-points-example-projects-etc" target="_blank" rel="noopener noreferrer">exempelprogram</a> för att lära dig om tRPC v10. För projekt som redan använde tRPC v9, <a href="https://trpc.io/docs/v10/migrate-from-v9-to-v10" target="_blank" rel="noopener noreferrer">besök migrationsguiden till v10</a>.</p>
<h2 class="anchor anchorWithStickyNavbar_MYIC" id="översikt-över-förändringar">Översikt över förändringar<a href="https://trpc.io/sv/blog/announcing-trpc-10#%C3%B6versikt-%C3%B6ver-f%C3%B6r%C3%A4ndringar" class="hash-link" aria-label="Direktlänk till Översikt över förändringar" title="Direktlänk till Översikt över förändringar">​</a></h2>
<p>v10 är tRPC<!-- -->:s<!-- --> största släpp någonsin. Det här är första gången vi gjort fundamentala förändringar i tRPC<!-- -->:s<!-- --> struktur och vi tror att dessa förbättringar öppnar nya möjligheter för snabbrörliga team som arbetar med banbrytande applikationer.</p>
<h3 class="anchor anchorWithStickyNavbar_MYIC" id="förbättrad-utvecklarupplevelse">Förbättrad utvecklarupplevelse<a href="https://trpc.io/sv/blog/announcing-trpc-10#f%C3%B6rb%C3%A4ttrad-utvecklarupplevelse" class="hash-link" aria-label="Direktlänk till Förbättrad utvecklarupplevelse" title="Direktlänk till Förbättrad utvecklarupplevelse">​</a></h3>
<p>tRPC v10 omfamnar din IDE. Vi vill enhetliggöra dina typer – men vi har också förenat din frontend, backend och redigeringsupplevelse i denna version.</p>
<p>Med v10 kan du:</p>
<ul>
<li>
<p>Använd <em>"Go to Definition"</em> för att hoppa direkt från din frontend-konsument till din backend-procedure</p>
</li>
<li>
<p>Använd <em>"Rename Symbol"</em> för att byta namn på ett inargument eller en procedure i hela applikationen</p>
</li>
<li>
<p><a href="https://trpc.io/docs/v10/infer-types" target="_blank" rel="noopener noreferrer">Hämta typer enklare</a> när du vill använda dina tRPC-typer manuellt i applikationen</p>
</li>
</ul>
<h3 class="anchor anchorWithStickyNavbar_MYIC" id="kraftfull-backend-ramverk">Kraftfull backend-ramverk<a href="https://trpc.io/sv/blog/announcing-trpc-10#kraftfull-backend-ramverk" class="hash-link" aria-label="Direktlänk till Kraftfull backend-ramverk" title="Direktlänk till Kraftfull backend-ramverk">​</a></h3>
<p>I v10 har vi reviderat syntaxen för hur du definierar backend-procedurer, vilket öppnar fler möjligheter att integrera din önskade logik på ett strukturerat sätt. Denna version av tRPC innehåller:</p>
<ul>
<li>
<p><a href="https://trpc.io/docs/v10/middlewares" target="_blank" rel="noopener noreferrer">Återanvändbara middlewares</a> med <a href="https://trpc.io/docs/v10/middlewares#context-extension" target="_blank" rel="noopener noreferrer">Context Extension</a></p>
</li>
<li>
<p><a href="https://trpc.io/docs/v10/procedures#reusable-base-publicprocedures" target="_blank" rel="noopener noreferrer">Kedjebara &amp; återanvändbara procedurer</a> med stöd för <a href="https://trpc.io/docs/v10/procedures#multiple-input-parsers" target="_blank" rel="noopener noreferrer">flera inputparsare</a></p>
</li>
<li>
<p>Flexibel <a href="https://trpc.io/docs/v10/error-handling" target="_blank" rel="noopener noreferrer">felhantering</a> med <a href="https://trpc.io/docs/v10/error-formatting" target="_blank" rel="noopener noreferrer">anpassad felformatering</a></p>
</li>
<li>
<p><a href="https://trpc.io/docs/v10/metadata" target="_blank" rel="noopener noreferrer">Procedurmetadata</a> för att utöka dina procedurer med mer information</p>
</li>
</ul>
<h3 class="anchor anchorWithStickyNavbar_MYIC" id="betydande-förbättring-av-typescript-prestanda">Betydande förbättring av TypeScript-prestanda<a href="https://trpc.io/sv/blog/announcing-trpc-10#betydande-f%C3%B6rb%C3%A4ttring-av-typescript-prestanda" class="hash-link" aria-label="Direktlänk till Betydande förbättring av TypeScript-prestanda" title="Direktlänk till Betydande förbättring av TypeScript-prestanda">​</a></h3>
<p>TypeScript gör det möjligt för utvecklare att göra fantastiska saker – men det kan komma med en kostnad. Många tekniker vi använder för att hålla dina typer strikta lägger stort tryck på TypeScript-kompilatorn. Vi fick feedback från communityn att större applikationer med tRPC v9 började drabbas av sämre prestanda i utvecklarnas IDE<!-- -->:er<!-- --> på grund av detta kompilatortryck.</p>
<p>Vårt mål är att förbättra utvecklarupplevelsen för applikationer i alla storlekar. I v10 har vi dramatiskt förbättrat TypeScript-prestandan (särskilt med TS inkrementell kompilering) så att din editor fortsätter vara snabb.</p>
<h2 class="anchor anchorWithStickyNavbar_MYIC" id="gradvis-migrering">Gradvis migrering<a href="https://trpc.io/sv/blog/announcing-trpc-10#gradvis-migrering" class="hash-link" aria-label="Direktlänk till Gradvis migrering" title="Direktlänk till Gradvis migrering">​</a></h2>
<p>Vi har också lagt ner mycket arbete på att göra migreringsprocessen så smidig som möjligt, inklusive en <code>interop()</code>-metod som ger (nästan) full bakåtkompatibilitet med v9-routrar. <a href="https://trpc.io/docs/v10/migrate-from-v9-to-v10" target="_blank" rel="noopener noreferrer">Läs migreringsguiden</a> för mer information.</p>
<p><a href="https://twitter.com/s4chinraja" target="_blank" rel="noopener noreferrer">Sachin</a> från kärnteamet har också skapat <a href="https://github.com/sachinraja/trpc-v10-migrate-codemod" target="_blank" rel="noopener noreferrer">en codemod</a> som kan göra mycket av det tunga lyftet vid migreringen åt dig.</p>
<h2 class="anchor anchorWithStickyNavbar_MYIC" id="ett-växande-ekosystem">Ett växande ekosystem<a href="https://trpc.io/sv/blog/announcing-trpc-10#ett-v%C3%A4xande-ekosystem" class="hash-link" aria-label="Direktlänk till Ett växande ekosystem" title="Direktlänk till Ett växande ekosystem">​</a></h2>
<p>Ett rikt utbud av underbibliotek fortsätter att växa fram kring tRPC. Här är några exempel:</p>
<ul>
<li>
<p><a href="https://github.com/jlalmes/trpc-openapi" target="_blank" rel="noopener noreferrer">trpc-openapi</a> för att enkelt skapa REST-kompatibla slutpunkter</p>
</li>
<li>
<p><a href="https://github.com/t3-oss/create-t3-app" target="_blank" rel="noopener noreferrer">create-t3-app</a> för att bootstrapa en fullstack Next.js-applikation med tRPC</p>
</li>
<li>
<p><a href="https://github.com/t3-oss/create-t3-turbo" target="_blank" rel="noopener noreferrer">create-t3-turbo</a> för att kickstarta din nästa React Native-app med tRPC</p>
</li>
<li>
<p><a href="https://github.com/jlalmes/trpc-chrome" target="_blank" rel="noopener noreferrer">trpc-chrome</a> för att bygga Chrome-tillägg med tRPC</p>
</li>
<li>
<p>Adapters för ramverk som <a href="https://github.com/OrJDev/solid-trpc" target="_blank" rel="noopener noreferrer">Solid</a>, <a href="https://github.com/icflorescu/trpc-sveltekit-example" target="_blank" rel="noopener noreferrer">Svelte</a> och <a href="https://github.com/michealroberts/usetrpc" target="_blank" rel="noopener noreferrer">Vue</a></p>
</li>
</ul>
<p>För fler plugins, exempel och adapters, <a href="https://trpc.io/awesome" target="_blank" rel="noopener noreferrer">besök Awesome tRPC-samlingen</a>.</p>
<h2 class="anchor anchorWithStickyNavbar_MYIC" id="tack">Tack!<a href="https://trpc.io/sv/blog/announcing-trpc-10#tack" class="hash-link" aria-label="Direktlänk till Tack!" title="Direktlänk till Tack!">​</a></h2>
<p>Kärnteamet och jag vill att ni ska veta: vi har precis börjat. Vi håller redan på att experimentera med <a href="https://github.com/reactjs/rfcs/pull/229" target="_blank" rel="noopener noreferrer">React Server Components</a> och Next.js 13.</p>
<p>Jag vill också ge ett jättestort tack till <a href="https://twitter.com/s4chinraja" target="_blank" rel="noopener noreferrer">Sachin</a>, <a href="https://twitter.com/jullerino" target="_blank" rel="noopener noreferrer">Julius</a>, <a href="https://twitter.com/jlalmes" target="_blank" rel="noopener noreferrer">James</a>, <a href="https://twitter.com/ixahmedxii" target="_blank" rel="noopener noreferrer">Ahmed</a>, <a href="https://twitter.com/trashh_dev" target="_blank" rel="noopener noreferrer">Chris</a>, <a href="https://twitter.com/theo" target="_blank" rel="noopener noreferrer">Theo</a>, <a href="https://twitter.com/shewDev" target="_blank" rel="noopener noreferrer">Anthony</a> och <a href="https://github.com/trpc/trpc#all-contributors" target="_blank" rel="noopener noreferrer">alla bidragsgivare som möjliggjort denna release</a>.</p>
<p>Tack för att ni använder och stödjer tRPC.</p>
<hr>
<h2 class="anchor anchorWithStickyNavbar_MYIC" id="slug-typescript-performance-lessonstitle-lärdomar-om-typescript-prestanda-vid-omstrukturering-för-v10authors-sachinraja">slug: typescript-performance-lessons
title: Lärdomar om TypeScript-prestanda vid omstrukturering för v10
authors: [sachinraja]<a href="https://trpc.io/sv/blog/announcing-trpc-10#slug-typescript-performance-lessonstitle-l%C3%A4rdomar-om-typescript-prestanda-vid-omstrukturering-f%C3%B6r-v10authors-sachinraja" class="hash-link" aria-label="Direktlänk till slug: typescript-performance-lessons
title: Lärdomar om TypeScript-prestanda vid omstrukturering för v10
authors: [sachinraja]" title="Direktlänk till slug: typescript-performance-lessons
title: Lärdomar om TypeScript-prestanda vid omstrukturering för v10
authors: [sachinraja]">​</a></h2>
<ul>
<li>
<p>Följ <a href="https://twitter.com/trpcio" target="_blank" rel="noopener noreferrer">@trpcio</a> på Twitter.</p>
</li>
<li>
<p>Gå med i vår <a href="https://trpc.io/discord" target="_blank" rel="noopener noreferrer">Discord-community</a></p>
</li>
<li>
<p><a href="https://trpc.io/#try-it-out" target="_blank" rel="noopener noreferrer">Testa tRPC direkt i webbläsaren</a></p>
</li>
</ul>
<a id="sponsor-button" href="https://trpc.io/sponsor" class="group flex h-12 w-max items-center gap-4 rounded-lg border-2 bg-zinc-200 px-4 py-2 transition hover:bg-zinc-100 dark:border-zinc-900 dark:bg-zinc-800 hover:dark:border-zinc-700 hover:dark:bg-zinc-900"><svg role="img" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg" class="aspect-square h-6 fill-pink-500 transition-transform duration-200 ease-in group-hover:scale-110"><path d="M17.625 1.499c-2.32 0-4.354 1.203-5.625 3.03-1.271-1.827-3.305-3.03-5.625-3.03C3.129 1.499 0 4.253 0 8.249c0 4.275 3.068 7.847 5.828 10.227a33.14 33.14 0 0 0 5.616 3.876l.028.017.008.003-.001.003c.163.085.342.126.521.125.179.001.358-.041.521-.125l-.001-.003.008-.003.028-.017a33.14 33.14 0 0 0 5.616-3.876C20.932 16.096 24 12.524 24 8.249c0-3.996-3.129-6.75-6.375-6.75zm-.919 15.275a30.766 30.766 0 0 1-4.703 3.316l-.004-.002-.004.002a30.955 30.955 0 0 1-4.703-3.316c-2.677-2.307-5.047-5.298-5.047-8.523 0-2.754 2.121-4.5 4.125-4.5 2.06 0 3.914 1.479 4.544 3.684.143.495.596.797 1.086.796.49.001.943-.302 1.085-.796.63-2.205 2.484-3.684 4.544-3.684 2.004 0 4.125 1.746 4.125 4.5 0 3.225-2.37 6.216-5.048 8.523z"></path></svg><span class="font-semibold text-zinc-900 no-underline dark:text-zinc-300">Bli sponsor!</span></a>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Introduktion till tRPC]]></title>
            <link>https://trpc.io/sv/blog/introducing-trpc</link>
            <guid>https://trpc.io/sv/blog/introducing-trpc</guid>
            <pubDate>Wed, 05 May 2021 00:00:00 GMT</pubDate>
            <description><![CDATA[Denna sida har översatts av PageTurner AI (beta). Inte officiellt godkänd av projektet.]]></description>
            <content:encoded><![CDATA[<div class="theme-admonition theme-admonition-info admonition_v5Ag alert alert--info"><div class="admonitionHeading_usrK"><span class="admonitionIcon_bgEp"><svg viewBox="0 0 14 16"><path fill-rule="evenodd" d="M7 2.3c3.14 0 5.7 2.56 5.7 5.7s-2.56 5.7-5.7 5.7A5.71 5.71 0 0 1 1.3 8c0-3.14 2.56-5.7 5.7-5.7zM7 1C3.14 1 0 4.14 0 8s3.14 7 7 7 7-3.14 7-7-3.14-7-7-7zm1 3H6v5h2V4zm0 6H6v2h2v-2z"></path></svg></span>Inofficiell Beta-översättning</div><div class="admonitionContent_e2NW"><p>Denna sida har översatts av <a href="https://page-turner.com/" target="_blank" rel="noopener noreferrer"><strong>PageTurner</strong></a> AI (beta). Inte officiellt godkänd av projektet.
Hittade du ett fel? <a href="https://feedback.page-turner.com/?repo_id=683d130a-1828-4b22-91cd-ef2d269ef3f5&amp;file_path=blog%2F2021-05-05-hello-world.mdx&amp;locale=sv" target="_blank" rel="noopener noreferrer">Rapportera problem →</a></p></div></div>
<p>tRPC ger dig typsäkerhet från server till klient (hela vägen), <em>utan att ens behöva deklarera typer</em>. På serversidan returnerar du bara data i en funktion, och på klientsidan använder du datan direkt baserat på endpoint-namnet.</p>
<!-- -->
<p>👋 Jag är Alex, eller "KATT" på GitHub, och jag vill berätta om ett bibliotek som heter <a href="https://trpc.io/" target="_blank" rel="noopener noreferrer">tRPC</a>. Jag har inte publicerat några artiklar om det ännu, så jag skriver bara den här introduktionen för att sätta igång (men vi har redan nått &gt;530 🌟 på GitHub). Räkna med artiklar och videointroduktioner framöver! Om du vill hålla dig uppdaterad eller ställa frågor kan du följa mig på Twitter <a href="https://twitter.com/alexdotjs" target="_blank" rel="noopener noreferrer">@alexdotjs</a>.</p>
<p><strong>Så här kan det se ut när du gör ett tRPC-anrop från klienten till en endpoint:</strong>
<img decoding="async" loading="lazy" src="https://assets.trpc.io/www/v9/trpcgif.gif" alt="Bildbeskrivning" class="img_Njog"></p>
<p>Jag har skapat ett bibliotek för React (<code>@trpc/react</code>) som bygger på det fantastiska react-query, men klientbiblioteket (<code>@trpc/client</code>) fungerar utan React (om du vill bygga ett bibliotek för Svelte/Vue/Angular/[..] - hör av dig!)</p>
<p>Ingen kodgenerering krävs och du kan enkelt lägga till det i ditt befintliga Next.js/CRA/Express-projekt.</p>
<h2 class="anchor anchorWithStickyNavbar_MYIC" id="exempel">Exempel<a href="https://trpc.io/sv/blog/introducing-trpc#exempel" class="hash-link" aria-label="Direktlänk till Exempel" title="Direktlänk till Exempel">​</a></h2>
<p>Här är ett exempel på en tRPC-procedur (kallas även endpoint) som heter <code>hello</code> och tar ett <code>string</code>-argument.</p>
<div><pre class="shiki min-light" style="background-color: #ffffff; color: #24292eff"><div class="language-id">tsx</div><div class="code-container"><code><div class="line"><span style="color: #D32F2F">const</span><span style="color: #24292EFF"> </span><span style="color: #1976D2">appRouter</span><span style="color: #24292EFF"> </span><span style="color: #D32F2F">=</span><span style="color: #24292EFF"> </span><span style="color: #1976D2">trpc</span><span style="color: #6F42C1">.router</span><span style="color: #24292EFF">()</span><span style="color: #6F42C1">.query</span><span style="color: #24292EFF">(</span><span style="color: #22863A">'hello'</span><span style="color: #212121">,</span><span style="color: #24292EFF"> {</span></div><div class="line"><span style="color: #24292EFF">  input</span><span style="color: #D32F2F">:</span><span style="color: #24292EFF"> </span><span style="color: #1976D2">z</span><span style="color: #6F42C1">.string</span><span style="color: #24292EFF">()</span><span style="color: #6F42C1">.optional</span><span style="color: #24292EFF">()</span><span style="color: #212121">,</span></div><div class="line"><span style="color: #24292EFF">  </span><span style="color: #6F42C1">resolve</span><span style="color: #D32F2F">:</span><span style="color: #24292EFF"> ({ input }) </span><span style="color: #D32F2F">=&gt;</span><span style="color: #24292EFF"> {</span></div><div class="line"><span style="color: #24292EFF">    </span><span style="color: #D32F2F">return</span><span style="color: #24292EFF"> {</span></div><div class="line"><span style="color: #24292EFF">      text</span><span style="color: #D32F2F">:</span><span style="color: #24292EFF"> </span><span style="color: #22863A">`hello </span><span style="color: #D32F2F">${</span><span style="color: #24292EFF">input </span><span style="color: #D32F2F">??</span><span style="color: #24292EFF"> </span><span style="color: #22863A">'world'</span><span style="color: #D32F2F">}</span><span style="color: #22863A">`</span><span style="color: #212121">,</span></div><div class="line"><span style="color: #24292EFF">    };</span></div><div class="line"><span style="color: #24292EFF">  }</span><span style="color: #212121">,</span></div><div class="line"><span style="color: #24292EFF">});</span></div><div class="line"></div><div class="line"><span style="color: #D32F2F">export</span><span style="color: #24292EFF"> </span><span style="color: #D32F2F">type</span><span style="color: #24292EFF"> </span><span style="color: #6F42C1">AppRouter</span><span style="color: #24292EFF"> </span><span style="color: #D32F2F">=</span><span style="color: #24292EFF"> </span><span style="color: #D32F2F">typeof</span><span style="color: #24292EFF"> appRouter;</span></div></code></div><button type="button" aria-label="Copy code to clipboard" class="copy-button" onclick="navigator.clipboard.writeText(this.previousSibling.innerText),this.classList.add(&quot;copied&quot;),this.textContent=&quot;Copied&quot;,setTimeout(()=>{this.classList.remove(&quot;copied&quot;),this.textContent=&quot;Copy&quot;},2e3)">Copy</button></pre>
<pre class="shiki github-dark" style="background-color: #0d1117; color: #c9d1d9"><div class="language-id">tsx</div><div class="code-container"><code><div class="line"><span style="color: #FF7B72">const</span><span style="color: #C9D1D9"> </span><span style="color: #79C0FF">appRouter</span><span style="color: #C9D1D9"> </span><span style="color: #FF7B72">=</span><span style="color: #C9D1D9"> trpc.</span><span style="color: #D2A8FF">router</span><span style="color: #C9D1D9">().</span><span style="color: #D2A8FF">query</span><span style="color: #C9D1D9">(</span><span style="color: #A5D6FF">'hello'</span><span style="color: #C9D1D9">, {</span></div><div class="line"><span style="color: #C9D1D9">  input: z.</span><span style="color: #D2A8FF">string</span><span style="color: #C9D1D9">().</span><span style="color: #D2A8FF">optional</span><span style="color: #C9D1D9">(),</span></div><div class="line"><span style="color: #C9D1D9">  </span><span style="color: #D2A8FF">resolve</span><span style="color: #C9D1D9">: ({ </span><span style="color: #FFA657">input</span><span style="color: #C9D1D9"> }) </span><span style="color: #FF7B72">=&gt;</span><span style="color: #C9D1D9"> {</span></div><div class="line"><span style="color: #C9D1D9">    </span><span style="color: #FF7B72">return</span><span style="color: #C9D1D9"> {</span></div><div class="line"><span style="color: #C9D1D9">      text: </span><span style="color: #A5D6FF">`hello ${</span><span style="color: #C9D1D9">input</span><span style="color: #A5D6FF"> </span><span style="color: #FF7B72">??</span><span style="color: #A5D6FF"> </span><span style="color: #A5D6FF">'world'}`</span><span style="color: #C9D1D9">,</span></div><div class="line"><span style="color: #C9D1D9">    };</span></div><div class="line"><span style="color: #C9D1D9">  },</span></div><div class="line"><span style="color: #C9D1D9">});</span></div><div class="line"></div><div class="line"><span style="color: #FF7B72">export</span><span style="color: #C9D1D9"> </span><span style="color: #FF7B72">type</span><span style="color: #C9D1D9"> </span><span style="color: #FFA657">AppRouter</span><span style="color: #C9D1D9"> </span><span style="color: #FF7B72">=</span><span style="color: #C9D1D9"> </span><span style="color: #FF7B72">typeof</span><span style="color: #C9D1D9"> appRouter;</span></div></code></div><button type="button" aria-label="Copy code to clipboard" class="copy-button" onclick="navigator.clipboard.writeText(this.previousSibling.innerText),this.classList.add(&quot;copied&quot;),this.textContent=&quot;Copied&quot;,setTimeout(()=>{this.classList.remove(&quot;copied&quot;),this.textContent=&quot;Copy&quot;},2e3)">Copy</button></pre></div>
<p>Och här är en typsäker klient som använder datan:</p>
<div><pre class="shiki min-light" style="background-color: #ffffff; color: #24292eff"><div class="language-id">tsx</div><div class="code-container"><code><div class="line"><span style="color: #D32F2F">import</span><span style="color: #24292EFF"> </span><span style="color: #D32F2F">type</span><span style="color: #24292EFF"> { AppRouter } </span><span style="color: #D32F2F">from</span><span style="color: #24292EFF"> </span><span style="color: #22863A">'./server'</span><span style="color: #24292EFF">;</span></div><div class="line"></div><div class="line"><span style="color: #D32F2F">async</span><span style="color: #24292EFF"> </span><span style="color: #D32F2F">function</span><span style="color: #24292EFF"> </span><span style="color: #6F42C1">main</span><span style="color: #24292EFF">() {</span></div><div class="line"><span style="color: #24292EFF">  </span><span style="color: #D32F2F">const</span><span style="color: #24292EFF"> </span><span style="color: #1976D2">client</span><span style="color: #24292EFF"> </span><span style="color: #D32F2F">=</span><span style="color: #24292EFF"> </span><span style="color: #6F42C1">createTRPCClient</span><span style="color: #24292EFF">&lt;</span><span style="color: #6F42C1">AppRouter</span><span style="color: #24292EFF">&gt;({</span></div><div class="line"><span style="color: #24292EFF">    url</span><span style="color: #D32F2F">:</span><span style="color: #24292EFF"> </span><span style="color: #22863A">`http://localhost:2022`</span><span style="color: #212121">,</span></div><div class="line"><span style="color: #24292EFF">  });</span></div><div class="line"></div><div class="line"><span style="color: #24292EFF">  </span><span style="color: #D32F2F">const</span><span style="color: #24292EFF"> </span><span style="color: #1976D2">result</span><span style="color: #24292EFF"> </span><span style="color: #D32F2F">=</span><span style="color: #24292EFF"> </span><span style="color: #D32F2F">await</span><span style="color: #24292EFF"> </span><span style="color: #1976D2">client</span><span style="color: #6F42C1">.query</span><span style="color: #24292EFF">(</span><span style="color: #22863A">'hello'</span><span style="color: #212121">,</span><span style="color: #24292EFF"> </span><span style="color: #22863A">'@alexdotjs'</span><span style="color: #24292EFF">);</span></div><div class="line"><span style="color: #24292EFF">  </span><span style="color: #1976D2">console</span><span style="color: #6F42C1">.log</span><span style="color: #24292EFF">(result); </span><span style="color: #C2C3C5">// --&gt; { text: "hello @alexdotjs" }</span></div><div class="line"><span style="color: #24292EFF">}</span></div><div class="line"></div><div class="line"><span style="color: #6F42C1">main</span><span style="color: #24292EFF">();</span></div></code></div><button type="button" aria-label="Copy code to clipboard" class="copy-button" onclick="navigator.clipboard.writeText(this.previousSibling.innerText),this.classList.add(&quot;copied&quot;),this.textContent=&quot;Copied&quot;,setTimeout(()=>{this.classList.remove(&quot;copied&quot;),this.textContent=&quot;Copy&quot;},2e3)">Copy</button></pre>
<pre class="shiki github-dark" style="background-color: #0d1117; color: #c9d1d9"><div class="language-id">tsx</div><div class="code-container"><code><div class="line"><span style="color: #FF7B72">import</span><span style="color: #C9D1D9"> </span><span style="color: #FF7B72">type</span><span style="color: #C9D1D9"> { AppRouter } </span><span style="color: #FF7B72">from</span><span style="color: #C9D1D9"> </span><span style="color: #A5D6FF">'./server'</span><span style="color: #C9D1D9">;</span></div><div class="line"></div><div class="line"><span style="color: #FF7B72">async</span><span style="color: #C9D1D9"> </span><span style="color: #FF7B72">function</span><span style="color: #C9D1D9"> </span><span style="color: #D2A8FF">main</span><span style="color: #C9D1D9">() {</span></div><div class="line"><span style="color: #C9D1D9">  </span><span style="color: #FF7B72">const</span><span style="color: #C9D1D9"> </span><span style="color: #79C0FF">client</span><span style="color: #C9D1D9"> </span><span style="color: #FF7B72">=</span><span style="color: #C9D1D9"> </span><span style="color: #D2A8FF">createTRPCClient</span><span style="color: #C9D1D9">&lt;</span><span style="color: #FFA657">AppRouter</span><span style="color: #C9D1D9">&gt;({</span></div><div class="line"><span style="color: #C9D1D9">    url: </span><span style="color: #A5D6FF">`http://localhost:2022`</span><span style="color: #C9D1D9">,</span></div><div class="line"><span style="color: #C9D1D9">  });</span></div><div class="line"></div><div class="line"><span style="color: #C9D1D9">  </span><span style="color: #FF7B72">const</span><span style="color: #C9D1D9"> </span><span style="color: #79C0FF">result</span><span style="color: #C9D1D9"> </span><span style="color: #FF7B72">=</span><span style="color: #C9D1D9"> </span><span style="color: #FF7B72">await</span><span style="color: #C9D1D9"> client.</span><span style="color: #D2A8FF">query</span><span style="color: #C9D1D9">(</span><span style="color: #A5D6FF">'hello'</span><span style="color: #C9D1D9">, </span><span style="color: #A5D6FF">'@alexdotjs'</span><span style="color: #C9D1D9">);</span></div><div class="line"><span style="color: #C9D1D9">  console.</span><span style="color: #D2A8FF">log</span><span style="color: #C9D1D9">(result); </span><span style="color: #8B949E">// --&gt; { text: "hello @alexdotjs" }</span></div><div class="line"><span style="color: #C9D1D9">}</span></div><div class="line"></div><div class="line"><span style="color: #D2A8FF">main</span><span style="color: #C9D1D9">();</span></div></code></div><button type="button" aria-label="Copy code to clipboard" class="copy-button" onclick="navigator.clipboard.writeText(this.previousSibling.innerText),this.classList.add(&quot;copied&quot;),this.textContent=&quot;Copied&quot;,setTimeout(()=>{this.classList.remove(&quot;copied&quot;),this.textContent=&quot;Copy&quot;},2e3)">Copy</button></pre></div>
<p><strong>Det är allt du behöver för typsäkerhet!</strong> <code>result</code> härleds automatiskt från vad servern returnerar. Indata härleds också från validerarens returvärde, så datan är säker att använda direkt – faktiskt måste du <em>alltid</em> skicka indata genom en validerare (tRPC fungerar out-of-the-box med zod/yup/egna validerare).</p>
<p>Här är en CodeSandbox-länk där du kan testa exemplet ovan: <a href="https://githubbox.com/trpc/trpc/tree/main/examples/standalone-server" target="_blank" rel="noopener noreferrer">https://githubbox.com/trpc/trpc/tree/main/examples/standalone-server</a> (titta på terminalutmatningen istället för förhandsgranskningen!)</p>
<p><strong><em>Va? Importerar jag kod från min server till klienten?</em> - Nej, det gör du faktiskt inte</strong></p>
<p>Även om det kan se ut så, delas ingen kod från server till klient; TypeScripts <code>import type</code> "[...] importerar endast deklarationer för typannoteringar. Den tas alltid bort helt och hållet och lämnar inga spår vid körning." – en funktion tillagd i TypeScript 3.8 - <a href="https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-8.html#:~:text=import%20type%20only%20imports%20declarations,also%20erased%20from%20TypeScript's%20output." target="_blank" rel="noopener noreferrer">läs i TypeScript-dokumentationen</a>.</p>
<p>Ingen kodgenerering är inblandad – du kan implementera detta direkt i din applikation om du har ett sätt att dela typer mellan server och klient (förhoppningsvis använder du redan en monorepo).</p>
<h2 class="anchor anchorWithStickyNavbar_MYIC" id="men-vi-har-precis-börjat">Men vi har precis börjat!<a href="https://trpc.io/sv/blog/introducing-trpc#men-vi-har-precis-b%C3%B6rjat" class="hash-link" aria-label="Direktlänk till Men vi har precis börjat!" title="Direktlänk till Men vi har precis börjat!">​</a></h2>
<p>Jag nämnde tidigare att det finns ett React-bibliotek. Så här använder du datan i React:</p>
<div><pre class="shiki min-light" style="background-color: #ffffff; color: #24292eff"><div class="language-id">tsx</div><div class="code-container"><code><div class="line"><span style="color: #D32F2F">const</span><span style="color: #24292EFF"> { </span><span style="color: #1976D2">data</span><span style="color: #24292EFF"> } </span><span style="color: #D32F2F">=</span><span style="color: #24292EFF"> </span><span style="color: #1976D2">trpc</span><span style="color: #6F42C1">.useQuery</span><span style="color: #24292EFF">([</span><span style="color: #22863A">'hello'</span><span style="color: #212121">,</span><span style="color: #24292EFF"> </span><span style="color: #22863A">'@alexdotjs'</span><span style="color: #24292EFF">]);</span></div></code></div><button type="button" aria-label="Copy code to clipboard" class="copy-button" onclick="navigator.clipboard.writeText(this.previousSibling.innerText),this.classList.add(&quot;copied&quot;),this.textContent=&quot;Copied&quot;,setTimeout(()=>{this.classList.remove(&quot;copied&quot;),this.textContent=&quot;Copy&quot;},2e3)">Copy</button></pre>
<pre class="shiki github-dark" style="background-color: #0d1117; color: #c9d1d9"><div class="language-id">tsx</div><div class="code-container"><code><div class="line"><span style="color: #FF7B72">const</span><span style="color: #C9D1D9"> { </span><span style="color: #79C0FF">data</span><span style="color: #C9D1D9"> } </span><span style="color: #FF7B72">=</span><span style="color: #C9D1D9"> trpc.</span><span style="color: #D2A8FF">useQuery</span><span style="color: #C9D1D9">([</span><span style="color: #A5D6FF">'hello'</span><span style="color: #C9D1D9">, </span><span style="color: #A5D6FF">'@alexdotjs'</span><span style="color: #C9D1D9">]);</span></div></code></div><button type="button" aria-label="Copy code to clipboard" class="copy-button" onclick="navigator.clipboard.writeText(this.previousSibling.innerText),this.classList.add(&quot;copied&quot;),this.textContent=&quot;Copied&quot;,setTimeout(()=>{this.classList.remove(&quot;copied&quot;),this.textContent=&quot;Copy&quot;},2e3)">Copy</button></pre></div>
<p>...och du får typsäker data på klienten.</p>
<p>Du kan implementera tRPC redan idag i ditt befintliga projekt (det finns adaptrar för Express/Next.js). Det fungerar utmärkt med CRA och bör fungera med React Native också. Det är inte ens knutet till React – om du vill skapa ett bibliotek för Svelte eller Vue, kontakta mig.</p>
<h2 class="anchor anchorWithStickyNavbar_MYIC" id="hur-hanterar-man-datamutationer">Hur hanterar man datamutationer?<a href="https://trpc.io/sv/blog/introducing-trpc#hur-hanterar-man-datamutationer" class="hash-link" aria-label="Direktlänk till Hur hanterar man datamutationer?" title="Direktlänk till Hur hanterar man datamutationer?">​</a></h2>
<p>Mutationer är lika enkla som queries – de är faktiskt samma sak under huven, men exponeras som syntaktisk socker och genererar HTTP POST-förfrågningar istället för GET.</p>
<p>Här är ett lite mer komplext exempel med en databas, hämtat från vårt TodoMVC-exempel på todomvc.trpc.io / <a href="https://github.com/trpc/trpc/tree/main/examples/next-prisma-todomvc" target="_blank" rel="noopener noreferrer">https://github.com/trpc/trpc/tree/main/examples/next-prisma-todomvc</a></p>
<div><pre class="shiki min-light" style="background-color: #ffffff; color: #24292eff"><div class="language-id">tsx</div><div class="code-container"><code><div class="line"><span style="color: #D32F2F">const</span><span style="color: #24292EFF"> </span><span style="color: #1976D2">todoRouter</span><span style="color: #24292EFF"> </span><span style="color: #D32F2F">=</span><span style="color: #24292EFF"> </span><span style="color: #6F42C1">createRouter</span><span style="color: #24292EFF">()</span><span style="color: #6F42C1">.mutation</span><span style="color: #24292EFF">(</span><span style="color: #22863A">'add'</span><span style="color: #212121">,</span><span style="color: #24292EFF"> {</span></div><div class="line"><span style="color: #24292EFF">  input</span><span style="color: #D32F2F">:</span><span style="color: #24292EFF"> </span><span style="color: #1976D2">z</span><span style="color: #6F42C1">.object</span><span style="color: #24292EFF">({</span></div><div class="line"><span style="color: #24292EFF">    id</span><span style="color: #D32F2F">:</span><span style="color: #24292EFF"> </span><span style="color: #1976D2">z</span><span style="color: #6F42C1">.string</span><span style="color: #24292EFF">()</span><span style="color: #6F42C1">.uuid</span><span style="color: #24292EFF">()</span><span style="color: #212121">,</span></div><div class="line"><span style="color: #24292EFF">    data</span><span style="color: #D32F2F">:</span><span style="color: #24292EFF"> </span><span style="color: #1976D2">z</span><span style="color: #6F42C1">.object</span><span style="color: #24292EFF">({</span></div><div class="line"><span style="color: #24292EFF">      completed</span><span style="color: #D32F2F">:</span><span style="color: #24292EFF"> </span><span style="color: #1976D2">z</span><span style="color: #6F42C1">.boolean</span><span style="color: #24292EFF">()</span><span style="color: #6F42C1">.optional</span><span style="color: #24292EFF">()</span><span style="color: #212121">,</span></div><div class="line"><span style="color: #24292EFF">      text</span><span style="color: #D32F2F">:</span><span style="color: #24292EFF"> </span><span style="color: #1976D2">z</span><span style="color: #6F42C1">.string</span><span style="color: #24292EFF">()</span><span style="color: #6F42C1">.min</span><span style="color: #24292EFF">(</span><span style="color: #1976D2">1</span><span style="color: #24292EFF">)</span><span style="color: #6F42C1">.optional</span><span style="color: #24292EFF">()</span><span style="color: #212121">,</span></div><div class="line"><span style="color: #24292EFF">    })</span><span style="color: #212121">,</span></div><div class="line"><span style="color: #24292EFF">  })</span><span style="color: #212121">,</span></div><div class="line"><span style="color: #24292EFF">  </span><span style="color: #D32F2F">async</span><span style="color: #24292EFF"> </span><span style="color: #6F42C1">resolve</span><span style="color: #24292EFF">({ ctx</span><span style="color: #212121">,</span><span style="color: #24292EFF"> input }) {</span></div><div class="line"><span style="color: #24292EFF">    </span><span style="color: #D32F2F">const</span><span style="color: #24292EFF"> { </span><span style="color: #1976D2">id</span><span style="color: #212121">,</span><span style="color: #24292EFF"> </span><span style="color: #1976D2">data</span><span style="color: #24292EFF"> } </span><span style="color: #D32F2F">=</span><span style="color: #24292EFF"> input;</span></div><div class="line"><span style="color: #24292EFF">    </span><span style="color: #D32F2F">const</span><span style="color: #24292EFF"> </span><span style="color: #1976D2">todo</span><span style="color: #24292EFF"> </span><span style="color: #D32F2F">=</span><span style="color: #24292EFF"> </span><span style="color: #D32F2F">await</span><span style="color: #24292EFF"> </span><span style="color: #1976D2">ctx</span><span style="color: #6F42C1">.</span><span style="color: #1976D2">task</span><span style="color: #6F42C1">.update</span><span style="color: #24292EFF">({</span></div><div class="line"><span style="color: #24292EFF">      where</span><span style="color: #D32F2F">:</span><span style="color: #24292EFF"> { id }</span><span style="color: #212121">,</span></div><div class="line"><span style="color: #24292EFF">      data</span><span style="color: #212121">,</span></div><div class="line"><span style="color: #24292EFF">    });</span></div><div class="line"><span style="color: #24292EFF">    </span><span style="color: #D32F2F">return</span><span style="color: #24292EFF"> todo;</span></div><div class="line"><span style="color: #24292EFF">  }</span><span style="color: #212121">,</span></div><div class="line"><span style="color: #24292EFF">});</span></div></code></div><button type="button" aria-label="Copy code to clipboard" class="copy-button" onclick="navigator.clipboard.writeText(this.previousSibling.innerText),this.classList.add(&quot;copied&quot;),this.textContent=&quot;Copied&quot;,setTimeout(()=>{this.classList.remove(&quot;copied&quot;),this.textContent=&quot;Copy&quot;},2e3)">Copy</button></pre>
<pre class="shiki github-dark" style="background-color: #0d1117; color: #c9d1d9"><div class="language-id">tsx</div><div class="code-container"><code><div class="line"><span style="color: #FF7B72">const</span><span style="color: #C9D1D9"> </span><span style="color: #79C0FF">todoRouter</span><span style="color: #C9D1D9"> </span><span style="color: #FF7B72">=</span><span style="color: #C9D1D9"> </span><span style="color: #D2A8FF">createRouter</span><span style="color: #C9D1D9">().</span><span style="color: #D2A8FF">mutation</span><span style="color: #C9D1D9">(</span><span style="color: #A5D6FF">'add'</span><span style="color: #C9D1D9">, {</span></div><div class="line"><span style="color: #C9D1D9">  input: z.</span><span style="color: #D2A8FF">object</span><span style="color: #C9D1D9">({</span></div><div class="line"><span style="color: #C9D1D9">    id: z.</span><span style="color: #D2A8FF">string</span><span style="color: #C9D1D9">().</span><span style="color: #D2A8FF">uuid</span><span style="color: #C9D1D9">(),</span></div><div class="line"><span style="color: #C9D1D9">    data: z.</span><span style="color: #D2A8FF">object</span><span style="color: #C9D1D9">({</span></div><div class="line"><span style="color: #C9D1D9">      completed: z.</span><span style="color: #D2A8FF">boolean</span><span style="color: #C9D1D9">().</span><span style="color: #D2A8FF">optional</span><span style="color: #C9D1D9">(),</span></div><div class="line"><span style="color: #C9D1D9">      text: z.</span><span style="color: #D2A8FF">string</span><span style="color: #C9D1D9">().</span><span style="color: #D2A8FF">min</span><span style="color: #C9D1D9">(</span><span style="color: #79C0FF">1</span><span style="color: #C9D1D9">).</span><span style="color: #D2A8FF">optional</span><span style="color: #C9D1D9">(),</span></div><div class="line"><span style="color: #C9D1D9">    }),</span></div><div class="line"><span style="color: #C9D1D9">  }),</span></div><div class="line"><span style="color: #C9D1D9">  </span><span style="color: #FF7B72">async</span><span style="color: #C9D1D9"> </span><span style="color: #D2A8FF">resolve</span><span style="color: #C9D1D9">({ </span><span style="color: #FFA657">ctx</span><span style="color: #C9D1D9">, </span><span style="color: #FFA657">input</span><span style="color: #C9D1D9"> }) {</span></div><div class="line"><span style="color: #C9D1D9">    </span><span style="color: #FF7B72">const</span><span style="color: #C9D1D9"> { </span><span style="color: #79C0FF">id</span><span style="color: #C9D1D9">, </span><span style="color: #79C0FF">data</span><span style="color: #C9D1D9"> } </span><span style="color: #FF7B72">=</span><span style="color: #C9D1D9"> input;</span></div><div class="line"><span style="color: #C9D1D9">    </span><span style="color: #FF7B72">const</span><span style="color: #C9D1D9"> </span><span style="color: #79C0FF">todo</span><span style="color: #C9D1D9"> </span><span style="color: #FF7B72">=</span><span style="color: #C9D1D9"> </span><span style="color: #FF7B72">await</span><span style="color: #C9D1D9"> ctx.task.</span><span style="color: #D2A8FF">update</span><span style="color: #C9D1D9">({</span></div><div class="line"><span style="color: #C9D1D9">      where: { id },</span></div><div class="line"><span style="color: #C9D1D9">      data,</span></div><div class="line"><span style="color: #C9D1D9">    });</span></div><div class="line"><span style="color: #C9D1D9">    </span><span style="color: #FF7B72">return</span><span style="color: #C9D1D9"> todo;</span></div><div class="line"><span style="color: #C9D1D9">  },</span></div><div class="line"><span style="color: #C9D1D9">});</span></div></code></div><button type="button" aria-label="Copy code to clipboard" class="copy-button" onclick="navigator.clipboard.writeText(this.previousSibling.innerText),this.classList.add(&quot;copied&quot;),this.textContent=&quot;Copied&quot;,setTimeout(()=>{this.classList.remove(&quot;copied&quot;),this.textContent=&quot;Copy&quot;},2e3)">Copy</button></pre></div>
<p>Och <strong>användningen med React</strong> ser ut så här:</p>
<div><pre class="shiki min-light" style="background-color: #ffffff; color: #24292eff"><div class="language-id">tsx</div><div class="code-container"><code><div class="line"><span style="color: #D32F2F">const</span><span style="color: #24292EFF"> </span><span style="color: #1976D2">addTask</span><span style="color: #24292EFF"> </span><span style="color: #D32F2F">=</span><span style="color: #24292EFF"> </span><span style="color: #1976D2">trpc</span><span style="color: #6F42C1">.useMutation</span><span style="color: #24292EFF">(</span><span style="color: #22863A">'todos.add'</span><span style="color: #24292EFF">);</span></div><div class="line"></div><div class="line"><span style="color: #D32F2F">return</span><span style="color: #24292EFF"> (</span></div><div class="line"><span style="color: #24292EFF">  &lt;&gt;</span></div><div class="line"><span style="color: #24292EFF">    &lt;</span><span style="color: #22863A">input</span></div><div class="line"><span style="color: #24292EFF">      </span><span style="color: #6F42C1">placeholder</span><span style="color: #D32F2F">=</span><span style="color: #22863A">"What needs to be done?"</span></div><div class="line"><span style="color: #24292EFF">      </span><span style="color: #6F42C1">onKeyDown</span><span style="color: #D32F2F">=</span><span style="color: #24292EFF">{(e) </span><span style="color: #D32F2F">=&gt;</span><span style="color: #24292EFF"> {</span></div><div class="line"><span style="color: #24292EFF">        </span><span style="color: #D32F2F">const</span><span style="color: #24292EFF"> </span><span style="color: #1976D2">text</span><span style="color: #24292EFF"> </span><span style="color: #D32F2F">=</span><span style="color: #24292EFF"> </span><span style="color: #1976D2">e</span><span style="color: #6F42C1">.</span><span style="color: #1976D2">currentTarget</span><span style="color: #6F42C1">.</span><span style="color: #1976D2">value</span><span style="color: #6F42C1">.trim</span><span style="color: #24292EFF">();</span></div><div class="line"><span style="color: #24292EFF">        </span><span style="color: #D32F2F">if</span><span style="color: #24292EFF"> (</span><span style="color: #1976D2">e</span><span style="color: #24292EFF">.key </span><span style="color: #D32F2F">===</span><span style="color: #24292EFF"> </span><span style="color: #22863A">'Enter'</span><span style="color: #24292EFF"> </span><span style="color: #D32F2F">&amp;&amp;</span><span style="color: #24292EFF"> text) {</span></div><div class="line"><span style="color: #24292EFF">          </span><span style="color: #1976D2">addTask</span><span style="color: #6F42C1">.mutate</span><span style="color: #24292EFF">({ text });</span></div><div class="line"><span style="color: #24292EFF">          </span><span style="color: #1976D2">e</span><span style="color: #24292EFF">.</span><span style="color: #1976D2">currentTarget</span><span style="color: #24292EFF">.value </span><span style="color: #D32F2F">=</span><span style="color: #24292EFF"> </span><span style="color: #22863A">''</span><span style="color: #24292EFF">;</span></div><div class="line"><span style="color: #24292EFF">        }</span></div><div class="line"><span style="color: #24292EFF">      }}</span></div><div class="line"><span style="color: #24292EFF">    /&gt;</span></div><div class="line"><span style="color: #24292EFF">  &lt;/&gt;</span></div><div class="line"><span style="color: #24292EFF">)</span></div></code></div><button type="button" aria-label="Copy code to clipboard" class="copy-button" onclick="navigator.clipboard.writeText(this.previousSibling.innerText),this.classList.add(&quot;copied&quot;),this.textContent=&quot;Copied&quot;,setTimeout(()=>{this.classList.remove(&quot;copied&quot;),this.textContent=&quot;Copy&quot;},2e3)">Copy</button></pre>
<pre class="shiki github-dark" style="background-color: #0d1117; color: #c9d1d9"><div class="language-id">tsx</div><div class="code-container"><code><div class="line"><span style="color: #FF7B72">const</span><span style="color: #C9D1D9"> </span><span style="color: #79C0FF">addTask</span><span style="color: #C9D1D9"> </span><span style="color: #FF7B72">=</span><span style="color: #C9D1D9"> trpc.</span><span style="color: #D2A8FF">useMutation</span><span style="color: #C9D1D9">(</span><span style="color: #A5D6FF">'todos.add'</span><span style="color: #C9D1D9">);</span></div><div class="line"></div><div class="line"><span style="color: #FF7B72">return</span><span style="color: #C9D1D9"> (</span></div><div class="line"><span style="color: #C9D1D9">  &lt;&gt;</span></div><div class="line"><span style="color: #C9D1D9">    &lt;</span><span style="color: #7EE787">input</span></div><div class="line"><span style="color: #C9D1D9">      </span><span style="color: #79C0FF">placeholder</span><span style="color: #FF7B72">=</span><span style="color: #A5D6FF">"What needs to be done?"</span></div><div class="line"><span style="color: #C9D1D9">      </span><span style="color: #79C0FF">onKeyDown</span><span style="color: #FF7B72">=</span><span style="color: #C9D1D9">{(</span><span style="color: #FFA657">e</span><span style="color: #C9D1D9">) </span><span style="color: #FF7B72">=&gt;</span><span style="color: #C9D1D9"> {</span></div><div class="line"><span style="color: #C9D1D9">        </span><span style="color: #FF7B72">const</span><span style="color: #C9D1D9"> </span><span style="color: #79C0FF">text</span><span style="color: #C9D1D9"> </span><span style="color: #FF7B72">=</span><span style="color: #C9D1D9"> e.currentTarget.value.</span><span style="color: #D2A8FF">trim</span><span style="color: #C9D1D9">();</span></div><div class="line"><span style="color: #C9D1D9">        </span><span style="color: #FF7B72">if</span><span style="color: #C9D1D9"> (e.key </span><span style="color: #FF7B72">===</span><span style="color: #C9D1D9"> </span><span style="color: #A5D6FF">'Enter'</span><span style="color: #C9D1D9"> </span><span style="color: #FF7B72">&amp;&amp;</span><span style="color: #C9D1D9"> text) {</span></div><div class="line"><span style="color: #C9D1D9">          addTask.</span><span style="color: #D2A8FF">mutate</span><span style="color: #C9D1D9">({ text });</span></div><div class="line"><span style="color: #C9D1D9">          e.currentTarget.value </span><span style="color: #FF7B72">=</span><span style="color: #C9D1D9"> </span><span style="color: #A5D6FF">''</span><span style="color: #C9D1D9">;</span></div><div class="line"><span style="color: #C9D1D9">        }</span></div><div class="line"><span style="color: #C9D1D9">      }}</span></div><div class="line"><span style="color: #C9D1D9">    /&gt;</span></div><div class="line"><span style="color: #C9D1D9">  &lt;/&gt;</span></div><div class="line"><span style="color: #C9D1D9">)</span></div></code></div><button type="button" aria-label="Copy code to clipboard" class="copy-button" onclick="navigator.clipboard.writeText(this.previousSibling.innerText),this.classList.add(&quot;copied&quot;),this.textContent=&quot;Copied&quot;,setTimeout(()=>{this.classList.remove(&quot;copied&quot;),this.textContent=&quot;Copy&quot;},2e3)">Copy</button></pre></div>
<h2 class="anchor anchorWithStickyNavbar_MYIC" id="slut-för-nu">Slut, för nu.<a href="https://trpc.io/sv/blog/introducing-trpc#slut-f%C3%B6r-nu" class="hash-link" aria-label="Direktlänk till Slut, för nu." title="Direktlänk till Slut, för nu.">​</a></h2>
<h2 class="anchor anchorWithStickyNavbar_MYIC" id="slut-för-tillfället">Slut, för tillfället.<a href="https://trpc.io/sv/blog/introducing-trpc#slut-f%C3%B6r-tillf%C3%A4llet" class="hash-link" aria-label="Direktlänk till Slut, för tillfället." title="Direktlänk till Slut, för tillfället.">​</a></h2>
<ul>
<li>
<p>Skapa kontext för inkommande förfrågningar för användarspecifik data som injiceras som beroende i resolver-funktionerna - <a href="https://trpc.io/sv/docs/v9/context">länk</a></p>
</li>
<li>
<p>Stöd för middleware i routrar - <a href="https://trpc.io/sv/docs/v9/middlewares">länk</a></p>
</li>
<li>
<p>Slå samman routrar (du vill förmodligen inte ha all din backenddata i en fil) - <a href="https://trpc.io/sv/docs/v9/merging-routers">länk</a></p>
</li>
<li>
<p>Enklaste server-side rendering du någonsin sett i React med vår <code>@trpc/next</code>-adapter - <a href="https://trpc.io/sv/docs/v9/">länk</a></p>
</li>
<li>
<p>Typsäker felformatering - <a href="https://trpc.io/sv/docs/v9/error-formatting">länk</a></p>
</li>
<li>
<p>Datatransformatorer (använd Date/Map/Set-objekt över nätet) - <a href="https://trpc.io/sv/docs/v9/data-transformers">länk</a></p>
</li>
<li>
<p>Hjälpfunktioner för React Query</p>
</li>
</ul>
<p>Om du vill komma igång finns det några exempel i <a href="https://trpc.io/sv/docs/v9/nextjs">Komma igång med Next.js</a>.</p>
<p><a href="https://twitter.com/alexdotjs" target="_blank" rel="noopener noreferrer">Följ mig på Twitter för uppdateringar!</a></p>]]></content:encoded>
        </item>
    </channel>
</rss>