<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
    <id>https://trpc.io/es/blog</id>
    <title>tRPC Blog</title>
    <updated>2025-03-21T00:00:00.000Z</updated>
    <generator>https://github.com/jpmonette/feed</generator>
    <link rel="alternate" href="https://trpc.io/es/blog"/>
    <subtitle>tRPC Blog</subtitle>
    <icon>https://trpc.io/es/img/favicon.ico</icon>
    <entry>
        <title type="html"><![CDATA[Anunciando tRPC v11]]></title>
        <id>https://trpc.io/es/blog/announcing-trpc-v11</id>
        <link href="https://trpc.io/es/blog/announcing-trpc-v11"/>
        <updated>2025-03-21T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[Esta página fue traducida por PageTurner AI (beta). No está respaldada oficialmente por el proyecto.]]></summary>
        <content type="html"><![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>Traducción Beta No Oficial</div><div class="admonitionContent_e2NW"><p>Esta página fue traducida por <a href="https://page-turner.com/" target="_blank" rel="noopener noreferrer"><strong>PageTurner</strong></a> AI (beta). No está respaldada oficialmente por el proyecto.
¿Encontraste un error? <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=es" target="_blank" rel="noopener noreferrer">Reportar problema →</a></p></div></div>
<!-- -->
<p>Aunque tRPC v11 ha estado listo para producción desde hace mucho tiempo a través de la etiqueta <code>@next</code>, nos hemos vuelto un poco adictos a añadir nuevas funciones sin ser estrictos con el versionado semántico. ¡Hoy estamos emocionados de <em>finalmente</em> quitarnos la venda y anunciar el lanzamiento oficial de tRPC v11!</p>
<!-- -->
<p>Desde nuestro último lanzamiento de versión principal en noviembre de 2022, la comunidad de tRPC ha experimentado un crecimiento sustancial:</p>
<ul>
<li>
<p>Ahora tenemos más de <a href="https://github.com/trpc/trpc" target="_blank" rel="noopener noreferrer">35,000 estrellas en GitHub</a></p>
</li>
<li>
<p><a href="https://trpc.io/discord" target="_blank" rel="noopener noreferrer">Una comunidad en Discord</a> con más de 5,000 miembros</p>
</li>
<li>
<p><a href="https://www.npmjs.com/package/@trpc/server" target="_blank" rel="noopener noreferrer">Más de 700k descargas semanales en npm</a></p>
</li>
<li>
<p><a href="https://github.com/trpc/trpc/graphs/contributors" target="_blank" rel="noopener noreferrer">Cientos de contribuyentes</a></p>
</li>
<li>
<p><a href="https://trpc.io/awesome" target="_blank" rel="noopener noreferrer">Un ecosistema <em>increíble</em> de extensiones, ejemplos y contenido</a></p>
</li>
</ul>
<p>Con el lanzamiento de tRPC v11, nos complace compartir que v11 ya se está utilizando en producción en muchos proyectos grandes de TypeScript gracias a su evolución estable en nuestro canal <code>@next</code></p>
<p>Para nuevos proyectos, puedes comenzar con una <a href="https://trpc.io/awesome#-starting-points-example-projects-etc" target="_blank" rel="noopener noreferrer">aplicación de ejemplo</a> para aprender sobre tRPC v11. Para proyectos que aún usan tRPC v10, <a href="https://trpc.io/docs/v11/migrate-from-v10-to-v11" target="_blank" rel="noopener noreferrer">visita la guía de migración a v11</a>.</p>
<h2 class="anchor anchorWithStickyNavbar_MYIC" id="resumen-de-cambios">Resumen de cambios<a href="https://trpc.io/es/blog/announcing-trpc-v11#resumen-de-cambios" class="hash-link" aria-label="Enlace directo a Resumen de cambios" title="Enlace directo a Resumen de cambios">​</a></h2>
<p>v11 es en gran medida compatible con versiones anteriores de v10, pero trae muchas características nuevas y mejoras. Estos son los aspectos más destacados:</p>
<h3 class="anchor anchorWithStickyNavbar_MYIC" id="soporte-para-tanstack-query-v5">Soporte para TanStack Query v5<a href="https://trpc.io/es/blog/announcing-trpc-v11#soporte-para-tanstack-query-v5" class="hash-link" aria-label="Enlace directo a Soporte para TanStack Query v5" title="Enlace directo a Soporte para TanStack Query v5">​</a></h3>
<p>Cuando se lanzó <a href="https://tanstack.com/blog/announcing-tanstack-query-v5" target="_blank" rel="noopener noreferrer">TanStack Query v5</a>, requirió algunos cambios de ruptura en la integración de react-query de tRPC. Esto ha estado disponible desde bastante temprano a través de la etiqueta <code>@next</code>, pero ahora se lanza oficialmente. Muchos proyectos ya han optado por actualizar y están disfrutando de los beneficios del soporte completo para React Suspense y muchas otras mejoras. Para orientación sobre cómo migrar tu código de cliente React de tRPC, puedes seguir <a href="https://tanstack.com/query/v5/docs/react/guides/migrating-to-v5" target="_blank" rel="noopener noreferrer">la propia guía de migración de TanStack Query</a>.</p>
<h3 class="anchor anchorWithStickyNavbar_MYIC" id="nueva-integración-con-tanstack-react-query">Nueva integración con TanStack React Query<a href="https://trpc.io/es/blog/announcing-trpc-v11#nueva-integraci%C3%B3n-con-tanstack-react-query" class="hash-link" aria-label="Enlace directo a Nueva integración con TanStack React Query" title="Enlace directo a Nueva integración con TanStack React Query">​</a></h3>
<p><a href="https://trpc.io/es/blog/introducing-tanstack-react-query-client">Ver la publicación del blog</a></p>
<h3 class="anchor anchorWithStickyNavbar_MYIC" id="soporte-para-formdata--tipos-de-contenido-no-json">Soporte para FormData / Tipos de contenido no JSON<a href="https://trpc.io/es/blog/announcing-trpc-v11#soporte-para-formdata--tipos-de-contenido-no-json" class="hash-link" aria-label="Enlace directo a Soporte para FormData / Tipos de contenido no JSON" title="Enlace directo a Soporte para FormData / Tipos de contenido no JSON">​</a></h3>
<p>Una de las funciones más solicitadas es la capacidad de enviar y recibir más que solo datos JSON. Con tRPC v11, ahora puedes enviar y recibir datos en varios <a href="https://trpc.io/es/docs/server/non-json-content-types">tipos de contenido no JSON</a>, <code>FormData</code>, y tipos binarios como <code>Blob</code>, <code>File</code> y <code>Uint8Array</code>. Puedes encontrar un ejemplo de cómo usar estos tipos de contenido <a href="https://github.com/trpc/trpc/tree/next/examples/minimal-content-types" target="_blank" rel="noopener noreferrer">aquí</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="componentes-de-servidor-de-react--app-router-de-nextjs">Componentes de Servidor de React / App Router de Next.js<a href="https://trpc.io/es/blog/announcing-trpc-v11#componentes-de-servidor-de-react--app-router-de-nextjs" class="hash-link" aria-label="Enlace directo a Componentes de Servidor de React / App Router de Next.js" title="Enlace directo a Componentes de Servidor de React / App Router de Next.js">​</a></h3>
<p>Aunque has podido usar tRPC con Next.js App Router desde el primer día, ya sea:</p>
<ul>
<li>
<p>usando un enfoque centrado en el servidor con async/await usando <a href="https://trpc.io/es/docs/server/server-side-calls"><code>createCaller</code></a> o <a href="https://trpc.io/es/docs/client/vanilla"><code>createTRPCClient</code></a>,</p>
</li>
<li>
<p>o un enfoque centrado en el cliente usando la <a href="https://trpc.io/es/docs/client/react">integración de React Query</a> y hooks del lado del cliente,</p>
</li>
</ul>
<p>La transición entre ambos enfoques ha presentado ciertas dificultades. Los patrones de revalidación se mezclan al mantener diferentes formas de obtener datos, resultando en una experiencia de desarrollo menos ideal que no cumple con los estándares de tRPC.</p>
<p>Para solucionarlo, hemos mejorado el soporte para React Server Components (RSC) y añadido helpers de prefetch para facilitar el aprovechamiento del poder de los RSC ejecutándose exclusivamente en el servidor, combinado con la altamente dinámica caché del lado cliente de React Query.
Ahora puedes iniciar la ejecución de un procedimiento en el servidor dentro de un RSC, continuar con la promesa pendiente en el cliente, e hidratar automáticamente la caché de React Query en el lado cliente. Esto te permite construir aplicaciones altamente dinámicas sin sufrir retrasos en cascada entre servidor y cliente. Puedes leer más en nuestra <a href="https://trpc.io/es/docs/client/react/server-components">documentación de Server Components</a>.</p>
<p>Además de estos nuevos patrones de prefetch, hemos añadido soporte experimental para funciones de servidor. Puedes leer más sobre esto en <a href="https://trpc.io/es/blog/trpc-actions">nuestro blog post</a>. Planeamos seguir iterando en esta característica a medida que las funciones de servidor se conviertan en un patrón más establecido en el ecosistema.</p>
<p>También hemos colaborado con el <a href="https://x.com/tannerlinsley/status/1844500352655335522" target="_blank" rel="noopener noreferrer">equipo de TanStack para ayudar a diseñar las APIs de sus funciones de servidor</a>. Nuestro objetivo es extraer partes del potente sistema de middleware de tRPC en un paquete separado que pueda usarse en todo el ecosistema.</p>
<h3 class="anchor anchorWithStickyNavbar_MYIC" id="respuestas-en-streaming-para-queries-y-mutations">Respuestas en Streaming para Queries y Mutations<a href="https://trpc.io/es/blog/announcing-trpc-v11#respuestas-en-streaming-para-queries-y-mutations" class="hash-link" aria-label="Enlace directo a Respuestas en Streaming para Queries y Mutations" title="Enlace directo a Respuestas en Streaming para Queries y Mutations">​</a></h3>
<p>Hemos introducido <a href="https://trpc.io/es/docs/client/links/httpBatchStreamLink">httpBatchStreamLink</a>, que permite transmitir respuestas de queries en streaming. Esto es útil al trabajar con grandes conjuntos de datos o cuando necesitas procesar datos en tiempo real y enviarlos al frontend progresivamente. No es un reemplazo para suscripciones, sino otra opción en tu caja de herramientas.</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="definiciones-abreviadas-de-routers">Definiciones Abreviadas de Routers<a href="https://trpc.io/es/blog/announcing-trpc-v11#definiciones-abreviadas-de-routers" class="hash-link" aria-label="Enlace directo a Definiciones Abreviadas de Routers" title="Enlace directo a Definiciones Abreviadas de Routers">​</a></h3>
<p>Hemos introducido una nueva sintaxis abreviada para definir tu router que simplifica el proceso de definir tus rutas. <a href="https://trpc.io/es/docs/server/routers#inline-sub-router">Documentación</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="suscripciones-server-sent-events-y-otras-mejoras">Suscripciones: Server-Sent Events y otras Mejoras<a href="https://trpc.io/es/blog/announcing-trpc-v11#suscripciones-server-sent-events-y-otras-mejoras" class="hash-link" aria-label="Enlace directo a Suscripciones: Server-Sent Events y otras Mejoras" title="Enlace directo a Suscripciones: Server-Sent Events y otras Mejoras">​</a></h3>
<ul>
<li>
<p>tRPC v11 introduce una nueva forma de manejar suscripciones usando <a href="https://trpc.io/es/docs/server/subscriptions#websockets-or-server-sent-events">Server-Sent Events (SSE)</a>. Es una excelente manera de manejar actualizaciones en tiempo real sin necesidad de WebSockets más complejos. Recomendamos usar este enfoque como primera opción.</p>
</li>
<li>
<p>Hemos añadido soporte para <a href="https://trpc.io/es/docs/server/subscriptions#cleanup-of-side-effects">generadores JavaScript en suscripciones</a>. Esto permite escribir manejadores de suscripción más complejos que pueden producir múltiples valores en el tiempo y limpiar al finalizar, de forma muy natural en JS.</p>
</li>
<li>
<p>Las suscripciones ahora admiten <a href="https://trpc.io/es/docs/server/subscriptions#output-validation">validación de salida</a>, mejorando la seguridad de tipos en tus manejadores.</p>
</li>
</ul>
<h3 class="anchor anchorWithStickyNavbar_MYIC" id="adiós-al-modo-interop-de-v9">Adiós al modo <code>.interop()</code> de v9<a href="https://trpc.io/es/blog/announcing-trpc-v11#adi%C3%B3s-al-modo-interop-de-v9" class="hash-link" aria-label="Enlace directo a adiós-al-modo-interop-de-v9" title="Enlace directo a adiós-al-modo-interop-de-v9">​</a></h3>
<p>En tRPC v10, introdujimos el modo <code>.interop()</code> para facilitar la migración desde v9. Con tRPC v11, hemos eliminado el modo <code>.interop()</code>. Si aún usas <code>.interop()</code>, puedes seguir la <a href="https://trpc.io/docs/v10/migrate-from-v9-to-v10" target="_blank" rel="noopener noreferrer">guía de migración v10</a> para completar tu transición a las APIs actuales de tRPC.</p>
<h2 class="anchor anchorWithStickyNavbar_MYIC" id="migrando-a-v11">Migrando a v11<a href="https://trpc.io/es/blog/announcing-trpc-v11#migrando-a-v11" class="hash-link" aria-label="Enlace directo a Migrando a v11" title="Enlace directo a Migrando a v11">​</a></h2>
<p>Si actualmente usas tRPC v10, sigue la <a href="https://trpc.io/es/docs/migrate-from-v10-to-v11">guía de migración</a> para actualizar a v11. La guía cubre todos los cambios disruptivos y nuevas características de v11.</p>
<h2 class="anchor anchorWithStickyNavbar_MYIC" id="gracias">¡Gracias!<a href="https://trpc.io/es/blog/announcing-trpc-v11#gracias" class="hash-link" aria-label="Enlace directo a ¡Gracias!" title="Enlace directo a ¡Gracias!">​</a></h2>
<p>De parte de todo el Core Team de tRPC, gracias por usar y apoyar tRPC.</p>
<hr>
<hr>
<h2 class="anchor anchorWithStickyNavbar_MYIC" id="slug-typescript-performance-lessonstitle-lecciones-de-rendimiento-en-typescript-durante-la-refactorización-para-v10authors-sachinraja">slug: typescript-performance-lessons
title: Lecciones de rendimiento en TypeScript durante la refactorización para v10
authors: [sachinraja]<a href="https://trpc.io/es/blog/announcing-trpc-v11#slug-typescript-performance-lessonstitle-lecciones-de-rendimiento-en-typescript-durante-la-refactorizaci%C3%B3n-para-v10authors-sachinraja" class="hash-link" aria-label="Enlace directo a slug: typescript-performance-lessons
title: Lecciones de rendimiento en TypeScript durante la refactorización para v10
authors: [sachinraja]" title="Enlace directo a slug: typescript-performance-lessons
title: Lecciones de rendimiento en TypeScript durante la refactorización para v10
authors: [sachinraja]">​</a></h2>
<ul>
<li>
<p>Síguenos en Twitter: <a href="https://twitter.com/trpcio" target="_blank" rel="noopener noreferrer">@trpcio</a></p>
</li>
<li>
<p>Únete a nuestra <a href="https://trpc.io/discord" target="_blank" rel="noopener noreferrer">comunidad de Discord</a></p>
</li>
<li>
<p><a href="https://trpc.io/#try-it-out" target="_blank" rel="noopener noreferrer">Prueba tRPC en tu navegador</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">¡Conviértete en Sponsor!</span></a>]]></content>
        <author>
            <name>tRPC</name>
            <uri>https://trpc.io/</uri>
        </author>
    </entry>
    <entry>
        <title type="html"><![CDATA[Presentamos la nueva integración de TanStack React Query]]></title>
        <id>https://trpc.io/es/blog/introducing-tanstack-react-query-client</id>
        <link href="https://trpc.io/es/blog/introducing-tanstack-react-query-client"/>
        <updated>2025-02-17T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[Esta página fue traducida por PageTurner AI (beta). No está respaldada oficialmente por el proyecto.]]></summary>
        <content type="html"><![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>Traducción Beta No Oficial</div><div class="admonitionContent_e2NW"><p>Esta página fue traducida por <a href="https://page-turner.com/" target="_blank" rel="noopener noreferrer"><strong>PageTurner</strong></a> AI (beta). No está respaldada oficialmente por el proyecto.
¿Encontraste un error? <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=es" target="_blank" rel="noopener noreferrer">Reportar problema →</a></p></div></div>
<p>Nos complace anunciar que la nueva integración de TanStack React Query para tRPC ya está disponible en la versión <code>next</code> de tRPC. Comparada con nuestra <a href="https://trpc.io/es/docs/client/react">integración clásica de React Query</a>, es más simple y más nativa de TanStack Query, ya que utiliza directamente las interfaces <a href="https://tanstack.com/query/latest/docs/framework/react/guides/query-options" target="_blank" rel="noopener noreferrer">QueryOptions</a> y <a href="https://tanstack.com/query/latest/docs/framework/react/guides/mutations" target="_blank" rel="noopener noreferrer">MutationOptions</a> propias de TanStack React Query, en lugar de envolver <code>useQuery</code> y <code>useMutation</code> con nuestro propio cliente.</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>Con este nuevo cliente eliminamos una capa de abstracción que suele causar confusión a los nuevos usuarios, ofreciendo en su lugar una forma más directa de trabajar con TanStack React Query que resultará inmediatamente familiar para quienes siguen la propia <a href="https://tanstack.com/query/latest" target="_blank" rel="noopener noreferrer">documentación</a> de TanStack. Esto también significa que necesitamos menos documentación de tRPC para explicarlo, aunque por supuesto tenemos <a href="https://trpc.io/es/docs/client/tanstack-react-query/setup">documentación para comenzar</a>.</p>
<h2 class="anchor anchorWithStickyNavbar_MYIC" id="por-qué-este-cambio">¿Por qué este cambio?<a href="https://trpc.io/es/blog/introducing-tanstack-react-query-client#por-qu%C3%A9-este-cambio" class="hash-link" aria-label="Enlace directo a ¿Por qué este cambio?" title="Enlace directo a ¿Por qué este cambio?">​</a></h2>
<p>Puedes leer nuestra RFC original que motivó este cambio <a href="https://github.com/trpc/trpc/discussions/6240" target="_blank" rel="noopener noreferrer">aquí</a>, donde se explican los detalles. Algunas de nuestras razones principales son:</p>
<ul>
<li>
<p><strong>Simplicidad</strong>: El nuevo cliente es más simple y más nativo de TanStack Query, proporcionando fábricas para interfaces comunes como QueryKeys, QueryOptions y MutationOptions. Esto reduce la curva de aprendizaje ya que puedes seguir la <a href="https://tanstack.com/query/latest" target="_blank" rel="noopener noreferrer">documentación oficial de TanStack Query</a></p>
</li>
<li>
<p><strong>Familiaridad</strong>: El nuevo cliente resulta más familiar para quienes ya usan TanStack Query, permitiéndote usarlo para otras cargas de trabajo en tu aplicación sin forzar una sintaxis alternativa para tRPC</p>
</li>
<li>
<p><strong>React</strong>: Nuestra integración clásica de React Query incumple las reglas de los hooks; <a href="https://github.com/trpc/trpc/issues/2330" target="_blank" rel="noopener noreferrer">no puede ser correctamente analizada</a>, y fomenta patrones que fallarán con el React Compiler, como pasar hooks como props. En este sentido, el nuevo cliente es más idiomático para React</p>
</li>
<li>
<p><strong>Mantenibilidad</strong>: Un desafío en nuestro versionado ha sido mantener tRPC alineado con los cambios de TanStack Query, especialmente nuevas funciones añadidas al QueryClient. Al usar la pequeña superficie de interfaces nativas, podemos soportar React Query más fácilmente, siguiendo lo que los <a href="https://bsky.app/profile/tkdodo.eu/post/3lgizrcvjmc24" target="_blank" rel="noopener noreferrer">mantenedores de TanStack consideran mejores prácticas</a></p>
</li>
<li>
<p><strong>Retroalimentación</strong>: Como mencionamos, el cliente clásico suele causar dificultades a nuevos usuarios, pero además la retroalimentación sobre <a href="https://github.com/trpc/trpc/discussions/6240" target="_blank" rel="noopener noreferrer">la RFC</a> para este cliente fue abrumadoramente positiva, con la mayoría de usuarios que nos comentaron entusiasmados por usarlo. Claro que no todos están convencidos aún, así que mantendremos el cliente clásico</p>
</li>
</ul>
<h2 class="anchor anchorWithStickyNavbar_MYIC" id="qué-pasará-con-la-integración-clásica-de-trpc-react-query">¿Qué pasará con la integración clásica de tRPC React Query?<a href="https://trpc.io/es/blog/introducing-tanstack-react-query-client#qu%C3%A9-pasar%C3%A1-con-la-integraci%C3%B3n-cl%C3%A1sica-de-trpc-react-query" class="hash-link" aria-label="Enlace directo a ¿Qué pasará con la integración clásica de tRPC React Query?" title="Enlace directo a ¿Qué pasará con la integración clásica de tRPC React Query?">​</a></h2>
<p>¡No desaparecerá pronto! Nos comprometemos a mantenerla durante mucho tiempo, aunque no recibirá nuevas características significativas y la consideraremos estable.</p>
<p>Seguimos recomendando que los nuevos proyectos comiencen con la nueva integración de TanStack React Query, y que los proyectos existentes consideren migrar gradualmente.</p>
<h2 class="anchor anchorWithStickyNavbar_MYIC" id="cómo-migrar">¿Cómo migrar?<a href="https://trpc.io/es/blog/introducing-tanstack-react-query-client#c%C3%B3mo-migrar" class="hash-link" aria-label="Enlace directo a ¿Cómo migrar?" title="Enlace directo a ¿Cómo migrar?">​</a></h2>
<p>Aunque el cliente clásico se mantendrá durante mucho tiempo, recomendamos que los nuevos proyectos empiecen con el nuevo cliente y que los proyectos activos consideren migrar gradualmente.</p>
<p>Ambos clientes son compatibles entre sí y pueden coexistir en la misma aplicación, así que puedes migrar a tu propio ritmo. También estamos trabajando en un codemod al que <strong>nos encantaría</strong> recibir contribuciones de la comunidad. ¡Agradecemos a <a href="https://bsky.app/profile/reaper.is" target="_blank" rel="noopener noreferrer">@reaper</a> por sus contribuciones al codemod hasta ahora!</p>
<p>👉 <a href="https://trpc.io/es/docs/client/tanstack-react-query/migrating">Lee la documentación de migración</a></p>]]></content>
        <author>
            <name>Julius Marminge</name>
            <uri>https://www.jumr.dev/</uri>
        </author>
        <author>
            <name>Nick Lucas</name>
            <uri>https://bsky.app/profile/nicklucas.dev</uri>
        </author>
    </entry>
    <entry>
        <title type="html"><![CDATA[Uso de Server Actions con tRPC]]></title>
        <id>https://trpc.io/es/blog/trpc-actions</id>
        <link href="https://trpc.io/es/blog/trpc-actions"/>
        <updated>2024-05-23T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[Esta página fue traducida por PageTurner AI (beta). No está respaldada oficialmente por el proyecto.]]></summary>
        <content type="html"><![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>Traducción Beta No Oficial</div><div class="admonitionContent_e2NW"><p>Esta página fue traducida por <a href="https://page-turner.com/" target="_blank" rel="noopener noreferrer"><strong>PageTurner</strong></a> AI (beta). No está respaldada oficialmente por el proyecto.
¿Encontraste un error? <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=es" target="_blank" rel="noopener noreferrer">Reportar problema →</a></p></div></div>
<!-- -->
<p>El patrón de construcción (builder-pattern) para crear procedimientos, introducido en tRPC v10, ha sido muy valorado por la comunidad, y muchas bibliotecas han adoptado patrones similares.
Incluso se ha acuñado el término <code>tRPC like XYZ</code> como evidencia de la creciente popularidad de este patrón. De hecho, el otro día vi <a href="https://x.com/localhost_5173/status/1793259910723215835" target="_blank" rel="noopener noreferrer">a alguien preguntándose si había una forma de escribir aplicaciones CLI con una API similar a tRPC</a>.
Nota al margen: incluso puedes usar <a href="https://github.com/mmkal/trpc-cli" target="_blank" rel="noopener noreferrer">tRPC directamente para hacer esto</a>. Pero no es de eso de lo que venimos a hablar hoy, sino de cómo usar tRPC con server actions de Next.js.</p>
<!-- -->
<h2 class="anchor anchorWithStickyNavbar_MYIC" id="qué-es-una-server-action">¿Qué es una server action?<a href="https://trpc.io/es/blog/trpc-actions#qu%C3%A9-es-una-server-action" class="hash-link" aria-label="Enlace directo a ¿Qué es una server action?" title="Enlace directo a ¿Qué es una server action?">​</a></h2>
<p>Por si vives debajo de una roca y no te has mantenido al día con las últimas características de <a href="https://react.dev/reference/rsc/server-actions" target="_blank" rel="noopener noreferrer">React</a> y <a href="https://nextjs.org/docs/app/building-your-application/data-fetching/server-actions-and-mutations" target="_blank" rel="noopener noreferrer">Next.js</a>, las server actions te permiten escribir funciones regulares que se ejecutan en el servidor, importarlas en el cliente y llamarlas como si fueran funciones normales.
Podrías pensar que esto suena similar a tRPC, y es cierto. Según <a href="https://x.com/dan_abramov2" target="_blank" rel="noopener noreferrer">Dan Abramov</a>, las server actions son "tRPC como una característica del bundler":</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>Y esto es totalmente acertado: las server actions son similares a tRPC, al fin y al cabo ambas son <a href="https://en.wikipedia.org/wiki/Remote_procedure_call" target="_blank" rel="noopener noreferrer">RPCs</a>. Ambas te permiten escribir funciones en el backend y llamarlas con total seguridad de tipos en el frontend, con la capa de red abstraída.</p>
<p>Entonces, ¿dónde encaja tRPC? ¿Por qué necesitaría tanto tRPC como server actions? Las server actions son una primitiva, y como todas las primitivas, son bastante básicas y carecen de aspectos fundamentales para construir APIs. Para cualquier endpoint de API expuesto en la red, necesitas validar y autorizar solicitudes para asegurarte de que tu API no se use maliciosamente. Como mencionamos antes, la API de tRPC es apreciada por la comunidad, así que ¿no sería genial si pudiéramos usar tRPC para definir server actions y aprovechar todas las características increíbles que vienen incorporadas en tRPC, como validación de entrada, autenticación y autorización mediante middlewares, validación de salida, transformadores de datos, etc., etc.? Yo creo que sí, así que profundicemos.</p>
<h2 class="anchor anchorWithStickyNavbar_MYIC" id="definición-de-server-actions-con-trpc">Definición de server actions con tRPC<a href="https://trpc.io/es/blog/trpc-actions#definici%C3%B3n-de-server-actions-con-trpc" class="hash-link" aria-label="Enlace directo a Definición de server actions con tRPC" title="Enlace directo a Definición de server actions con 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>nota</div><div class="admonitionContent_e2NW"><p><strong>Prerrequisitos:</strong> Para usar server actions, necesitas utilizar el App Router de Next.js. Además, todas las funcionalidades de tRPC que usaremos solo están disponibles en tRPC v11, así que asegúrate de usar el canal beta de 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="Copiar código al portapapeles" class="copy-button">Copiar</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="Copiar código al portapapeles" class="copy-button">Copiar</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="Copiar código al portapapeles" class="copy-button">Copiar</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="Copiar código al portapapeles" class="copy-button">Copiar</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="Copiar código al portapapeles" class="copy-button">Copiar</button></pre></div></div></div></div></div>
<p>Empecemos inicializando tRPC y definiendo nuestro procedimiento base para server actions.
Usaremos el método <code>experimental_caller</code> del constructor de procedimientos, un nuevo método que te permite personalizar cómo se llama el procedimiento cuando se invoca como función. También usaremos el adaptador <code>experimental_nextAppDirCaller</code> para hacerlo compatible con Next.js. Este adaptador manejará casos donde la server action está envuelta en <code>useActionState</code> en el cliente, lo cual <a href="https://react.dev/reference/react/useActionState#my-action-can-no-longer-read-the-submitted-form-data" target="_blank" rel="noopener noreferrer">cambia la firma de llamada de la server action</a>.</p>
<p>También usaremos una propiedad <code>span</code> como <a href="https://trpc.io/es/docs/server/metadata">metadatos</a>, ya que no hay una ruta ordinaria como cuando usas un router (por ejemplo, <code>user.byId</code>). Puedes usar la propiedad span para diferenciar procedimientos, por ejemplo durante el registro de logs o la observabilidad.</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>A continuación, añadiremos <a href="https://trpc.io/es/docs/server/context">contexto</a>. Como no alojaremos un router usando un adaptador HTTP regular, no tendremos contexto inyectado mediante el método <code>createContext</code> del adaptador. En su lugar, usaremos un middleware para inyectar nuestro contexto. En este ejemplo, obtengamos el usuario actual desde la sesión e inyectémoslo en el contexto.</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>Por último, crearemos un procedimiento <code>protectedAction</code> que protegerá cualquier acción de usuarios no autenticados. Si ya tienes un middleware existente que hace esto, puedes usarlo, pero para este ejemplo definiré uno en línea.</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>Muy bien, escribamos una acción de servidor real. Crea un archivo <code>_actions.ts</code>, decóralo con la directiva <code>"use server"</code> y define tu acción.</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>¡Vaya! Es así de fácil definir una acción de servidor protegida contra usuarios no autenticados, con validación de entrada para prevenir ataques como inyecciones SQL. Importemos esta función en el cliente y llamémosla.</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="avanzando">Avanzando<a href="https://trpc.io/es/blog/trpc-actions#avanzando" class="hash-link" aria-label="Enlace directo a Avanzando" title="Enlace directo a Avanzando">​</a></h2>
<p>Usando los constructores de tRPC y su enfoque componible para definir procedimientos reutilizables, podemos crear fácilmente acciones de servidor más complejas. Aquí algunos ejemplos:</p>
<h3 class="anchor anchorWithStickyNavbar_MYIC" id="observabilidad">Observabilidad<a href="https://trpc.io/es/blog/trpc-actions#observabilidad" class="hash-link" aria-label="Enlace directo a Observabilidad" title="Enlace directo a Observabilidad">​</a></h3>
<p>Puedes usar el plugin de tRPC de <code>@baselime/node-opentelemtry</code> para añadir observabilidad con solo unas líneas de código:</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>Consulta la <a href="https://github.com/baselime/node-opentelemetry/blob/main/TRPC.md" target="_blank" rel="noopener noreferrer">Integración de tRPC con Baselime</a> para más información. Patrones similares deberían funcionar con cualquier plataforma de observabilidad que uses.</p>
<h3 class="anchor anchorWithStickyNavbar_MYIC" id="limitación-de-tasa-rate-limiting">Limitación de tasa (Rate Limiting)<a href="https://trpc.io/es/blog/trpc-actions#limitaci%C3%B3n-de-tasa-rate-limiting" class="hash-link" aria-label="Enlace directo a Limitación de tasa (Rate Limiting)" title="Enlace directo a Limitación de tasa (Rate Limiting)">​</a></h3>
<p>Puedes usar servicios como Unkey para limitar la tasa de tus acciones de servidor. Este es un ejemplo de acción protegida que usa Unkey para limitar solicitudes por usuario:</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>Lee más sobre cómo limitar la tasa de tus procedimientos tRPC en <a href="https://www.unkey.com/blog/ratelimit-trpc-routes" target="_blank" rel="noopener noreferrer">esta publicación del equipo de Unkey</a>.</p>
<p>Las posibilidades son infinitas, y seguro ya tienes muchos middlewares útiles en tus aplicaciones tRPC. Si no, ¡quizás encuentres algunos que puedas instalar con <code>npm install</code>!</p>
<h2 class="anchor anchorWithStickyNavbar_MYIC" id="conclusión">Conclusión<a href="https://trpc.io/es/blog/trpc-actions#conclusi%C3%B3n" class="hash-link" aria-label="Enlace directo a Conclusión" title="Enlace directo a Conclusión">​</a></h2>
<p>Las Acciones de Servidor no son una solución mágica. Para datos más dinámicos, conviene mantener datos en la caché de React Query del cliente y hacer mutaciones con <code>useMutation</code>. Esto es totalmente válido. Estas nuevas primitivas permiten adopción incremental: puedes migrar procedimientos individuales de tu API tRPC existente donde tenga sentido, sin reescribir toda la API.</p>
<p>Al definir acciones de servidor con tRPC, reutilizas lógica existente y eliges dónde exponer mutaciones como acciones de servidor o mutaciones tradicionales. Como desarrollador, decides qué patrones funcionan mejor. Si no usas tRPC actualmente, paquetes como <a href="https://github.com/TheEdoRan/next-safe-action" target="_blank" rel="noopener noreferrer">next-safe-action</a> o <a href="https://github.com/IdoPesok/zsa" target="_blank" rel="noopener noreferrer">zsa</a> también permiten crear acciones de servidor tipadas y validadas.</p>
<p>Para ver una aplicación usando esto en acción, visita <a href="https://trellix-trpc.vercel.app/" target="_blank" rel="noopener noreferrer">Trellix tRPC</a>, una app que creé recientemente usando estas primitivas.</p>
<h3 class="anchor anchorWithStickyNavbar_MYIC" id="qué-opinas-queremos-tu-feedback">¿Qué opinas? Queremos tu feedback<a href="https://trpc.io/es/blog/trpc-actions#qu%C3%A9-opinas-queremos-tu-feedback" class="hash-link" aria-label="Enlace directo a ¿Qué opinas? Queremos tu feedback" title="Enlace directo a ¿Qué opinas? Queremos tu feedback">​</a></h3>
<p>¿Qué te parece? Cuéntanos en <a href="https://github.com/trpc/trpc/discussions/5737" target="_blank" rel="noopener noreferrer">Github</a> y ayúdanos a llevar estas primitivas a un estado estable.</p>
<p>Queda trabajo pendiente, especialmente en manejo de errores. Next.js recomienda devolver errores, y queremos hacerlo con seguridad de tipos. Mira <a href="https://github.com/trpc/trpc/pull/5554" target="_blank" rel="noopener noreferrer">este PR en progreso de Alex</a> para avances iniciales.</p>
<p>¡Hasta la próxima, feliz codificación!</p>]]></content>
        <author>
            <name>Julius Marminge</name>
            <uri>https://www.jumr.dev/</uri>
        </author>
    </entry>
    <entry>
        <title type="html"><![CDATA[Creando un cliente tRPC pequeño]]></title>
        <id>https://trpc.io/es/blog/tinyrpc-client</id>
        <link href="https://trpc.io/es/blog/tinyrpc-client"/>
        <updated>2023-01-17T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[Esta página fue traducida por PageTurner AI (beta). No está respaldada oficialmente por el proyecto.]]></summary>
        <content type="html"><![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>Traducción Beta No Oficial</div><div class="admonitionContent_e2NW"><p>Esta página fue traducida por <a href="https://page-turner.com/" target="_blank" rel="noopener noreferrer"><strong>PageTurner</strong></a> AI (beta). No está respaldada oficialmente por el proyecto.
¿Encontraste un error? <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=es" target="_blank" rel="noopener noreferrer">Reportar problema →</a></p></div></div>
<p>¿Alguna vez te has preguntado cómo funciona tRPC? ¿Quizás quieres contribuir al proyecto pero te intimidan sus internos? El objetivo de esta publicación es familiarizarte con los detalles internos de tRPC mediante la creación de un cliente mínimo que cubra las partes principales de su funcionamiento.</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>información</div><div class="admonitionContent_e2NW"><p>Se recomienda que comprendas algunos conceptos centrales de TypeScript como genéricos, tipos condicionales, la palabra clave <code>extends</code> y recursión. Si no estás familiarizado con estos, te sugiero revisar el tutorial <a href="https://github.com/total-typescript/beginners-typescript-tutorial" target="_blank" rel="noopener noreferrer">TypeScript para principiantes</a> de <a href="https://twitter.com/mattpocockuk" target="_blank" rel="noopener noreferrer">Matt Pocock</a> para familiarizarte con estos conceptos antes de continuar.</p></div></div>
<h2 class="anchor anchorWithStickyNavbar_MYIC" id="visión-general">Visión general<a href="https://trpc.io/es/blog/tinyrpc-client#visi%C3%B3n-general" class="hash-link" aria-label="Enlace directo a Visión general" title="Enlace directo a Visión general">​</a></h2>
<p>Supongamos que tenemos un router tRPC simple con tres procedimientos que luce así:</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>El objetivo de nuestro cliente es imitar esta estructura de objetos en el lado del cliente para que podamos llamar procedimientos como:</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>Para lograr esto, tRPC usa una combinación de objetos <a href="https://mdn.io/proxy" target="_blank" rel="noopener noreferrer"><code>Proxy</code></a> y algo de magia TypeScript para aumentar la estructura de objetos con los métodos <code>.query</code> y <code>.mutate</code> en ellos, ¡lo que significa que en realidad te ENGÑAMOS sobre lo que estás haciendo (más sobre esto después) para brindar una excelente experiencia de desarrollo!</p>
<p>En términos generales, lo que queremos hacer es mapear <code>post.byId.query()</code> a una solicitud GET a nuestro servidor, y <code>post.create.mutate()</code> a una solicitud POST, y los tipos deberían propagarse completamente desde el backend al frontend. Entonces, ¿cómo lo hacemos?</p>
<h2 class="anchor anchorWithStickyNavbar_MYIC" id="implementando-un-pequeño-cliente-trpc">Implementando un pequeño cliente tRPC<a href="https://trpc.io/es/blog/tinyrpc-client#implementando-un-peque%C3%B1o-cliente-trpc" class="hash-link" aria-label="Enlace directo a Implementando un pequeño cliente tRPC" title="Enlace directo a Implementando un pequeño cliente tRPC">​</a></h2>
<h3 class="anchor anchorWithStickyNavbar_MYIC" id="️-la-magia-de-typescript">🧙‍♀️ La magia de TypeScript<a href="https://trpc.io/es/blog/tinyrpc-client#%EF%B8%8F-la-magia-de-typescript" class="hash-link" aria-label="Enlace directo a 🧙‍♀️ La magia de TypeScript" title="Enlace directo a 🧙‍♀️ La magia de TypeScript">​</a></h3>
<p>Comencemos con la divertida magia de TypeScript para desbloquear el increíble autocompletado y seguridad de tipos que todos conocemos y amamos al usar tRPC.</p>
<p>Necesitaremos usar tipos recursivos para poder inferir estructuras de router arbitrariamente profundas. Además, sabemos que queremos que nuestros procedimientos <code>post.byId</code> y <code>post.create</code> tengan los métodos <code>.query</code> y <code>.mutate</code> respectivamente; en tRPC llamamos a esto "decorar" los procedimientos. En <code>@trpc/server</code>, tenemos algunos ayudantes de inferencia que deducirán los tipos de entrada y salida de nuestros procedimientos con estos métodos resueltos, los cuales usaremos para inferir los tipos de estas funciones, ¡así que escribamos código!</p>
<p>Consideremos lo que queremos lograr para proporcionar autocompletado en rutas e inferencia de tipos de entrada y salida de los procedimientos:</p>
<ul>
<li>
<p>Si estamos en un router, queremos poder acceder a sus sub-routers y procedimientos (llegaremos a esto en un momento)</p>
</li>
<li>
<p>Si estamos en un procedimiento de query, queremos poder llamar <code>.query</code> en él</p>
</li>
<li>
<p>Si estamos en un procedimiento de mutación, queremos poder llamar <code>.mutate</code> en él</p>
</li>
<li>
<p>Si intentamos acceder a cualquier otra cosa, queremos obtener un error de tipo que indique que ese procedimiento no existe en el backend</p>
</li>
</ul>
<p>Así que creemos un tipo que haga esto por nosotros:</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>Usaremos algunos de los ayudantes de inferencia incorporados de tRPC para deducir los tipos de entrada y salida de nuestros procedimientos y definir el tipo <code>Resolver</code>.</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>Probemos esto en nuestro procedimiento <code>post.byId</code>:</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>¡Genial, eso es lo que esperábamos! Ahora podemos llamar <code>.query</code> en nuestro procedimiento y obtener los tipos de entrada y salida correctos inferidos.</p>
<p>Finalmente, crearemos un tipo que recorrerá recursivamente el router y decorará todos los procedimientos en el camino:</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>Analicemos un poco este tipo:</p>
<ol>
<li>
<p>Pasamos un <code>TRPCRouterRecord</code> al tipo como genérico, que es un tipo que contiene todos los procedimientos y sub-routers que existen en un router tRPC.</p>
</li>
<li>
<p>Iteramos sobre las claves del registro, que son los nombres de los procedimientos o routers, y hacemos lo siguiente:</p>
<ul>
<li>Si la clave apunta a un router, llamamos recursivamente al tipo en el registro de procedimientos de ese router, lo que decorará todos los procedimientos en ese router. Esto proporcionará autocompletado mientras navegamos por la ruta.</li>
<li>Si la clave apunta a un procedimiento, decoramos el procedimiento usando el tipo <code>DecorateProcedure</code> que creamos anteriormente.</li>
<li>Si la clave no apunta a un procedimiento ni a un router, asignamos el tipo <code>never</code>, que equivale a decir "esta clave no existe", lo que generará un error de tipo si intentamos acceder a ella.</li>
</ul>
</li>
</ol>
<h3 class="anchor anchorWithStickyNavbar_MYIC" id="-la-reasignación-mediante-proxy">🤯 La reasignación mediante Proxy<a href="https://trpc.io/es/blog/tinyrpc-client#-la-reasignaci%C3%B3n-mediante-proxy" class="hash-link" aria-label="Enlace directo a 🤯 La reasignación mediante Proxy" title="Enlace directo a 🤯 La reasignación mediante Proxy">​</a></h3>
<p>Ahora que tenemos todos los tipos configurados, necesitamos implementar la funcionalidad que aumentará la definición del router del servidor en el cliente para que podamos invocar los procedimientos como funciones normales.</p>
<p>Primero crearemos una función auxiliar para crear proxies recursivos: <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>información</div><div class="admonitionContent_e2NW"><p>Esta es casi la misma implementación usada en producción, excepto que no manejamos algunos casos extremos. <a href="https://github.com/trpc/trpc/blob/main/packages/server/src/shared/createProxy/index.ts" target="_blank" rel="noopener noreferrer">¡Compruébalo tú mismo</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>Esto parece un poco mágico, ¿qué hace exactamente?</p>
<ul>
<li>
<p>El método <code>get</code> maneja los accesos a propiedades como <code>post.byId</code>. La clave es el nombre de la propiedad que estamos accediendo: cuando escribimos <code>post</code>, nuestra <code>key</code> será <code>post</code>, y cuando escribamos <code>post.byId</code>, nuestra <code>key</code> será <code>byId</code>. El proxy recursivo combina todas estas claves en una ruta final, por ejemplo ["post", "byId", "query"], que podemos usar para determinar la URL a la que enviar la solicitud.</p>
</li>
<li>
<p>El método <code>apply</code> se llama cuando invocamos una función en el proxy, como <code>.query(args)</code>. Los <code>args</code> son los argumentos que pasamos a la función, así que cuando llamamos <code>post.byId.query(args)</code>, nuestros <code>args</code> serán nuestra entrada, que proporcionaremos como parámetros de consulta o cuerpo de la solicitud según el tipo de procedimiento. El <code>createRecursiveProxy</code> recibe una función de callback que mapearemos con <code>apply</code> usando la ruta y los argumentos.</p>
</li>
</ul>
<p>A continuación se muestra una representación visual de cómo funciona el proxy en la llamada <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="-integrando-todo">🧩 Integrando todo<a href="https://trpc.io/es/blog/tinyrpc-client#-integrando-todo" class="hash-link" aria-label="Enlace directo a 🧩 Integrando todo" title="Enlace directo a 🧩 Integrando todo">​</a></h3>
<p>Ahora que tenemos este helper y sabemos lo que hace, usémoslo para crear nuestro cliente. Proporcionaremos al <code>createRecursiveProxy</code> un callback que tomará la ruta y los argumentos para solicitar al servidor usando <code>fetch</code>. Necesitaremos agregar un genérico a la función que acepte cualquier tipo de router tRPC (<code>AnyTRPCRouter</code>), y luego convertiremos el tipo de retorno al tipo <code>DecorateRouterRecord</code> que creamos anteriormente:</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>Lo más notable aquí es que nuestra ruta está separada por <code>.</code> en lugar de <code>/</code>. Esto nos permite tener un único manejador de API en el servidor que procesará todas las solicitudes, y no uno para cada procedimiento. Si estás usando un framework con enrutamiento basado en archivos como Next.js, quizás reconozcas el archivo catchall <code>/api/trpc/[trpc].ts</code> que coincidirá con todas las rutas de los procedimientos.</p>
<p>También tenemos una anotación de tipo <code>TRPCResponse</code> en la solicitud <code>fetch</code>. Esto determina el formato de respuesta compatible con JSONRPC que devuelve el servidor. Puedes leer más sobre esto <a href="https://trpc.io/docs/rpc" target="_blank" rel="noopener noreferrer">aquí</a>. TL;DR: obtenemos un objeto <code>result</code> o <code>error</code>, que podemos usar para determinar si la solicitud fue exitosa o no, y manejar errores apropiadamente si algo salió mal.</p>
<p>¡Y eso es todo! Este es todo el código que necesitarás para llamar a tus procedimientos tRPC en el cliente como si fueran funciones locales. En la superficie, parece que simplemente estamos llamando a la función resolver de <code>publicProcedure.query / mutation</code> mediante accesos normales a propiedades, pero en realidad estamos cruzando un límite de red para poder usar bibliotecas del lado del servidor como Prisma sin filtrar credenciales de la base de datos.</p>
<h2 class="anchor anchorWithStickyNavbar_MYIC" id="pruébalo">¡Pruébalo!<a href="https://trpc.io/es/blog/tinyrpc-client#pru%C3%A9balo" class="hash-link" aria-label="Enlace directo a ¡Pruébalo!" title="Enlace directo a ¡Pruébalo!">​</a></h2>
<p>Ahora, crea el cliente proporcionando la URL de tu servidor y ¡obtendrás autocompletado completo y seguridad de tipos al llamar a tus procedimientos!</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>El código completo del cliente está disponible <a href="https://github.com/trpc/trpc/blob/main/packages/tests/showcase/tinyrpc.ts" target="_blank" rel="noopener noreferrer">aquí</a>, y las pruebas de uso <a href="https://github.com/trpc/trpc/blob/main/packages/tests/showcase/tinyrpc.test.ts" target="_blank" rel="noopener noreferrer">aquí</a>.</p>
<h2 class="anchor anchorWithStickyNavbar_MYIC" id="conclusión">Conclusión<a href="https://trpc.io/es/blog/tinyrpc-client#conclusi%C3%B3n" class="hash-link" aria-label="Enlace directo a Conclusión" title="Enlace directo a Conclusión">​</a></h2>
<p>Espero que hayas disfrutado este artículo y aprendido cómo funciona tRPC internamente. Probablemente no deberías usar este cliente minimalista en lugar de @trpc/client, que solo pesa unos pocos KB más pero ofrece mucha más flexibilidad:</p>
<ul>
<li>
<p>Opciones de query para señales de aborto, SSR, etc...</p>
</li>
<li>
<p>Enlaces</p>
</li>
<li>
<p>Agrupación de procedimientos (batching)</p>
</li>
<li>
<p>Soporte para WebSockets / suscripciones</p>
</li>
<li>
<p>Manejo elegante de errores</p>
</li>
<li>
<p>Transformadores de datos</p>
</li>
<li>
<p>Manejo de casos extremos como respuestas no compatibles con tRPC</p>
</li>
</ul>
<p>Tampoco cubrimos hoy el lado del servidor, quizás lo abordemos en un futuro artículo. Si tienes preguntas, no dudes en contactarme por <a href="https://twitter.com/jullerino" target="_blank" rel="noopener noreferrer">Twitter</a>.</p>]]></content>
        <author>
            <name>Julius Marminge</name>
            <uri>https://www.jumr.dev/</uri>
        </author>
    </entry>
    <entry>
        <title type="html"><![CDATA[Lecciones de rendimiento en TypeScript al refactorizar para v10]]></title>
        <id>https://trpc.io/es/blog/typescript-performance-lessons</id>
        <link href="https://trpc.io/es/blog/typescript-performance-lessons"/>
        <updated>2023-01-14T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[Esta página fue traducida por PageTurner AI (beta). No está respaldada oficialmente por el proyecto.]]></summary>
        <content type="html"><![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>Traducción Beta No Oficial</div><div class="admonitionContent_e2NW"><p>Esta página fue traducida por <a href="https://page-turner.com/" target="_blank" rel="noopener noreferrer"><strong>PageTurner</strong></a> AI (beta). No está respaldada oficialmente por el proyecto.
¿Encontraste un error? <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=es" target="_blank" rel="noopener noreferrer">Reportar problema →</a></p></div></div>
<p>No es ningún secreto que TypeScript es el motor detrás de la excelente DX que ofrece tRPC. La adopción de TypeScript se ha convertido en el estándar moderno para ofrecer grandes experiencias basadas en JavaScript, aunque esta mayor certeza en los tipos conlleva algunas compensaciones.</p>
<!-- -->
<p>No es ningún secreto que TypeScript es la fuerza impulsora detrás de la increíble experiencia de desarrollo (DX) que ofrece tRPC. La adopción de TypeScript es el estándar moderno para ofrecer excelentes experiencias basadas en JavaScript hoy en día, pero esta mayor certeza en torno a los tipos conlleva algunas compensaciones.</p>
<p>Hoy, el comprobador de tipos de TypeScript tiende a volverse lento (¡aunque versiones como <a href="https://devblogs.microsoft.com/typescript/announcing-typescript-4-9/#performance-improvements" target="_blank" rel="noopener noreferrer">TS 4.9</a> son prometedoras!). Las bibliotecas casi siempre contienen los conjuros de TypeScript más sofisticados en tu base de código, llevando tu compilador de TS a sus límites. Por esta razón, los autores de bibliotecas como nosotros debemos ser conscientes de nuestra contribución a esa carga y hacer todo lo posible para que tu IDE siga funcionando lo más rápido posible.</p>
<p>Durante la etapa de v9 de tRPC, comenzamos a recibir informes de desarrolladores indicando que sus grandes enrutadores tRPC estaban afectando negativamente a su verificador de tipos. Esta fue una experiencia nueva para tRPC, ya que vimos una adopción masiva durante la fase v9 de su desarrollo. Con más desarrolladores creando productos cada vez más grandes con tRPC, comenzaron a aparecer algunas grietas.</p>
<p>Mientras tRPC estaba en <code>v9</code>, comenzamos a recibir informes de desarrolladores de que sus grandes enrutadores tRPC empezaban a tener efectos perjudiciales en su comprobador de tipos. Esto fue una nueva experiencia para tRPC, ya que vimos una tremenda adopción durante la fase <code>v9</code> del desarrollo de tRPC. Con más desarrolladores creando productos cada vez más grandes con tRPC, comenzaron a aparecer algunas grietas.</p>
<p>Tu biblioteca puede no ser lenta <em>ahora</em>, pero es importante vigilar el rendimiento a medida que crece y cambia. Las pruebas automatizadas pueden eliminar una carga inmensa de la creación de bibliotecas (¡y de la construcción de aplicaciones!) al probar <em>programáticamente</em> tu código en cada commit.</p>
<p>Para tRPC, hacemos todo lo posible para garantizar esto mediante la <a href="https://github.com/trpc/trpc/blob/9fc2d06a8924da73e10b9d4497f3a1f53de706ed/scripts/generateBigBoi.ts" target="_blank" rel="noopener noreferrer">generación</a> y <a href="https://github.com/trpc/trpc/blob/9fc2d06a8924da73e10b9d4497f3a1f53de706ed/packages/tests/server/react/bigBoi.test.tsx" target="_blank" rel="noopener noreferrer">pruebas</a> de un enrutador con 3,500 procedimientos y 1,000 enrutadores. Pero esto solo prueba hasta dónde podemos forzar el compilador de TypeScript antes de que falle, no cuánto tiempo tarda la verificación de tipos. Probamos las tres partes de la biblioteca (servidor, cliente vanilla y cliente React) porque todas tienen rutas de código diferentes. En el pasado, hemos visto regresiones aisladas en una sección de la biblioteca y confiamos en nuestras pruebas para detectar estos comportamientos inesperados. (Todavía <a href="https://github.com/trpc/trpc/issues/2892" target="_blank" rel="noopener noreferrer">queremos hacer más</a> para medir los tiempos de compilación)</p>
<ul>
<li>
<p>Lentitud al verificar tipos con tsc</p>
</li>
<li>
<p>Tiempos de carga inicial elevados</p>
</li>
<li>
<p>Si el servidor de lenguaje de TypeScript tarda mucho en responder a cambios</p>
</li>
<li>
<p>Ser lento para la comprobación de tipos usando <code>tsc</code></p>
</li>
</ul>
<h2 class="anchor anchorWithStickyNavbar_MYIC" id="cómo-encontré-oportunidades-de-rendimiento-en-trpc">Cómo encontré oportunidades de rendimiento en tRPC<a href="https://trpc.io/es/blog/typescript-performance-lessons#c%C3%B3mo-encontr%C3%A9-oportunidades-de-rendimiento-en-trpc" class="hash-link" aria-label="Enlace directo a Cómo encontré oportunidades de rendimiento en tRPC" title="Enlace directo a Cómo encontré oportunidades de rendimiento en tRPC">​</a></h2>
<p>Siempre hay un equilibrio entre la precisión de TypeScript y el rendimiento del compilador. Ambas son preocupaciones importantes para otros desarrolladores, por lo que debemos ser extremadamente conscientes de cómo escribimos los tipos. ¿Podría una aplicación encontrar errores graves porque un tipo es "demasiado flexible"? ¿Vale la pena la ganancia de rendimiento?</p>
<p>¿Habrá siquiera una mejora de rendimiento significativa? Excelente pregunta.</p>
<h2 class="anchor anchorWithStickyNavbar_MYIC" id="cómo-encontré-oportunidades-de-rendimiento-en-trpc-1">Cómo encontré oportunidades de rendimiento en tRPC<a href="https://trpc.io/es/blog/typescript-performance-lessons#c%C3%B3mo-encontr%C3%A9-oportunidades-de-rendimiento-en-trpc-1" class="hash-link" aria-label="Enlace directo a Cómo encontré oportunidades de rendimiento en tRPC" title="Enlace directo a Cómo encontré oportunidades de rendimiento en tRPC">​</a></h2>
<p>Siempre hay un equilibrio entre la precisión de TypeScript y el rendimiento del compilador. Ambas son preocupaciones importantes para otros desarrolladores, por lo que debemos ser extremadamente conscientes de cómo escribimos los tipos. ¿Será posible que una aplicación encuentre errores graves porque un tipo determinado es "demasiado flexible"? ¿Vale la pena la ganancia en rendimiento?</p>
<p>¿Va a haber realmente una mejora de rendimiento significativa? Gran pregunta.</p>
<p>Así tracé el rendimiento de tRPC: Echemos un vistazo a <em>cómo</em> encontrar oportunidades para mejoras de rendimiento en código TypeScript. Repasaremos el proceso que seguí para crear <a href="https://github.com/trpc/trpc/pull/2716" target="_blank" rel="noopener noreferrer">PR #2716</a>, lo que resultó en una reducción del 59% en el tiempo de compilación de TS.</p>
<hr>
<hr>
<h2 class="anchor anchorWithStickyNavbar_MYIC" id="slug-typescript-performance-lessonstitle-lecciones-de-rendimiento-en-typescript-durante-la-refactorización-para-v10authors-sachinraja">slug: typescript-performance-lessons
title: Lecciones de rendimiento en TypeScript durante la refactorización para v10
authors: [sachinraja]<a href="https://trpc.io/es/blog/typescript-performance-lessons#slug-typescript-performance-lessonstitle-lecciones-de-rendimiento-en-typescript-durante-la-refactorizaci%C3%B3n-para-v10authors-sachinraja" class="hash-link" aria-label="Enlace directo a slug: typescript-performance-lessons
title: Lecciones de rendimiento en TypeScript durante la refactorización para v10
authors: [sachinraja]" title="Enlace directo a slug: typescript-performance-lessons
title: Lecciones de rendimiento en TypeScript durante la refactorización para v10
authors: [sachinraja]">​</a></h2>
<p>TypeScript incluye una <a href="https://github.com/microsoft/TypeScript/wiki/Performance-Tracing" target="_blank" rel="noopener noreferrer">herramienta de trazado</a> que ayuda a identificar cuellos de botella en tus tipos. No es perfecta, pero es la mejor opción disponible.</p>
<p>Es ideal probar tu biblioteca en una aplicación real para simular lo que experimentan los desarrolladores. Para tRPC, creé una aplicación básica con <a href="https://create.t3.gg/" target="_blank" rel="noopener noreferrer">T3</a> similar a lo que usan muchos de nuestros usuarios.</p>
<p>Así tracé el rendimiento de tRPC:</p>
<ol>
<li>
<p><a href="https://docs.npmjs.com/cli/commands/npm-link" target="_blank" rel="noopener noreferrer">Vincula localmente la biblioteca</a> a la aplicación de ejemplo. Esto permite probar cambios inmediatamente.</p>
</li>
<li>
<p>Ejecuta este comando en la app de ejemplo:</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>Obtendrás un archivo <code>trace/trace.json</code>. Ábrelo con una herramienta de análisis como <a href="https://ui.perfetto.dev/" target="_blank" rel="noopener noreferrer">Perfetto</a> o <code>chrome://tracing</code>.</p>
</li>
</ol>
<p>Aquí empieza lo interesante. Mi primer trazado mostró esto:
<img decoding="async" loading="lazy" src="https://assets.trpc.io/www/blog/2023-01-14-typescript-performance-lessons/trace-1.png" alt="Barra de trazado mostrando que src/pages/index.ts tardó 332ms en compilar" class="img_Njog"></p>
<p>Las barras más largas indican mayor tiempo de proceso. He seleccionado la barra verde superior para esta captura de pantalla, indicando que <code>src/pages/index.ts</code> era el cuello de botella. Bajo el campo <code>Duration</code>, verás que tomó 332ms: ¡una enorme cantidad de tiempo para la verificación de tipos! La barra azul <code>checkVariableDeclaration</code> revela que el compilador se enfocó en una variable. Al inspeccionarla:
<img decoding="async" loading="lazy" src="https://assets.trpc.io/www/blog/2023-01-14-typescript-performance-lessons/trace-2.png" alt="Info del trazado mostrando posición 275" class="img_Njog">
El campo <code>pos</code> indica la ubicación de la variable en el archivo. Al ir a esa posición en <code>src/pages/index.ts</code> se revela que el culpable era <code>utils = trpc.useContext()</code>!</p>
<p>¿Pero cómo? ¡Es solo un hook simple! Veamos el código:</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>Aparentemente nada complejo: solo un <code>useContext</code> y una invalidación de query. El problema debía estar más profundo. Examinemos los tipos:</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>Tenemos material para analizar. Primero entendamos qué hace este código.</p>
<p>Un tipo recursivo <code>DecoratedProcedureUtilsRecord</code> recorre todos los procedimientos del router y los "decora" (añade métodos) con utilidades de React Query como <a href="https://tanstack.com/query/v4/docs/framework/react/guides/query-invalidation" target="_blank" rel="noopener noreferrer"><code>invalidateQueries</code></a>.</p>
<p>En tRPC v10 aún soportamos routers <code>v9</code>, pero los clientes <code>v10</code> no pueden llamar a procedimientos de routers <code>v9</code>. Para cada procedimiento, verificamos si es de <code>v9</code> (<code>extends LegacyV9ProcedureTag</code>) y lo excluimos si es necesario. ¡Mucho trabajo para TypeScript... <strong>si no se evalúa perezosamente</strong>!</p>
<h3 class="anchor anchorWithStickyNavbar_MYIC" id="evaluación-perezosa">Evaluación perezosa<a href="https://trpc.io/es/blog/typescript-performance-lessons#evaluaci%C3%B3n-perezosa" class="hash-link" aria-label="Enlace directo a Evaluación perezosa" title="Enlace directo a Evaluación perezosa">​</a></h3>
<p>El problema aquí es que TypeScript está evaluando todo este código en el sistema de tipos, aunque no se use inmediatamente. Nuestro código solo utiliza <code>utils.r49.greeting.invalidate</code>, por lo que TypeScript solo debería necesitar descomponer la propiedad <code>r49</code> (un router), luego la propiedad <code>greeting</code> (un procedimiento) y finalmente la función <code>invalidate</code> para ese procedimiento. No se necesitan otros tipos en ese código, y determinar inmediatamente el tipo de cada método de utilidad de React Query para todos los procedimientos de tRPC ralentizaría innecesariamente a TypeScript. TypeScript difiere la evaluación de tipos para propiedades en <strong>objetos</strong> hasta que se usan directamente, así que teóricamente nuestro tipo debería tener evaluación perezosa... ¿verdad?</p>
<p>Bueno, no es <em>exactamente</em> un objeto. En realidad hay un tipo envolviendo todo: <code>OmitNeverKeys</code>. Este tipo es una utilidad que elimina las claves con valor <code>never</code> de un objeto. Aquí es donde eliminamos los procedimientos de v9 para que esas propiedades no aparezcan en Intellisense.</p>
<p>Pero esto crea un enorme problema de rendimiento. Forzamos a TypeScript a evaluar los valores de <strong>todos</strong> los tipos ahora para verificar si son <code>never</code>.</p>
<p>¿Cómo podemos solucionarlo? Cambiemos nuestros tipos para <em>hacer menos</em>.</p>
<h3 class="anchor anchorWithStickyNavbar_MYIC" id="hazlo-perezoso">Hazlo perezoso<a href="https://trpc.io/es/blog/typescript-performance-lessons#hazlo-perezoso" class="hash-link" aria-label="Enlace directo a Hazlo perezoso" title="Enlace directo a Hazlo perezoso">​</a></h3>
<p>Necesitamos que la API de <code>v10</code> se adapte a los routers heredados de <code>v9</code> de forma más elegante. Los nuevos proyectos de tRPC no deberían sufrir el rendimiento reducido de TypeScript en el <a href="https://trpc.io/es/docs/v10/migrate-from-v9-to-v10#using-interop">modo de interoperabilidad</a>.</p>
<p>La idea es reorganizar los tipos principales. Los procedimientos de <code>v9</code> son entidades diferentes a los de <code>v10</code>, por lo que no deberían compartir el mismo espacio en nuestro código de biblioteca. En el lado del servidor de tRPC, esto significó trabajo para almacenar los tipos en campos diferentes del router en lugar de un solo campo <code>record</code> (ver <code>DecoratedProcedureUtilsRecord</code> mencionado antes).</p>
<p>Implementamos un cambio para que los routers de <code>v9</code> inyecten sus procedimientos en un campo <code>legacy</code> al convertirse a routers de <code>v10</code>.</p>
<p>Tipos antiguos:</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>Si recuerdas el tipo <code>DecoratedProcedureUtilsRecord</code> anterior, verás que adjuntamos <code>LegacyV9ProcedureTag</code> aquí para diferenciar entre procedimientos de <code>v9</code> y <code>v10</code> a nivel de tipo y garantizar que los procedimientos de <code>v9</code> no sean llamados desde clientes de <code>v10</code>.</p>
<p>Tipos nuevos:</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>Ahora podemos eliminar <code>OmitNeverKeys</code> porque los procedimientos están preclasificados: el tipo de propiedad <code>record</code> de un router contendrá todos los procedimientos de <code>v10</code>, y su propiedad <code>legacy</code> contendrá los de <code>v9</code>. Ya no forzamos a TypeScript a evaluar completamente el enorme tipo <code>DecoratedProcedureUtilsRecord</code>. También podemos eliminar el filtrado para procedimientos de <code>v9</code> con <code>LegacyV9ProcedureTag</code>.</p>
<h2 class="anchor anchorWithStickyNavbar_MYIC" id="funcionó">¿Funcionó?<a href="https://trpc.io/es/blog/typescript-performance-lessons#funcion%C3%B3" class="hash-link" aria-label="Enlace directo a ¿Funcionó?" title="Enlace directo a ¿Funcionó?">​</a></h2>
<p>Nuevo seguimiento muestra que el cuello de botella se eliminó:
<img decoding="async" loading="lazy" src="https://assets.trpc.io/www/blog/2023-01-14-typescript-performance-lessons/trace-3.png" alt="Barra de seguimiento mostrando que src/pages/index.ts tardó 136ms en verificación de tipos" class="img_Njog"></p>
<p>¡Una mejora sustancial! El tiempo de verificación de tipos bajó de 332ms a 136ms 🤯. Puede parecer poco en perspectiva, pero es una gran victoria. 200ms es una cantidad pequeña una vez, pero considera:</p>
<ul>
<li>
<p>cuántas otras bibliotecas TS hay en un proyecto</p>
</li>
<li>
<p>cuántos desarrolladores usan tRPC actualmente</p>
</li>
<li>
<p>cuántas veces se reevalúan sus tipos en una sesión de trabajo</p>
</li>
</ul>
<p>Eso suma muchos 200ms acumulándose en un número muy grande.</p>
<p>Siempre buscamos oportunidades para mejorar la experiencia de desarrolladores TypeScript, ya sea con tRPC o problemas basados en TS en otros proyectos. Mencioname en <a href="https://twitter.com/s4chinraja" target="_blank" rel="noopener noreferrer">Twitter</a> si quieres hablar de TypeScript.</p>
<p>¡Gracias a <a href="https://twitter.com/anthonysheww" target="_blank" rel="noopener noreferrer">Anthony Shew</a> por ayudar a escribir esta publicación y a <a href="https://twitter.com/alexdotjs" target="_blank" rel="noopener noreferrer">Alex</a> por revisarla!</p>]]></content>
        <author>
            <name>Sachin Raja</name>
        </author>
    </entry>
    <entry>
        <title type="html"><![CDATA[Anunciando tRPC v10]]></title>
        <id>https://trpc.io/es/blog/announcing-trpc-10</id>
        <link href="https://trpc.io/es/blog/announcing-trpc-10"/>
        <updated>2022-11-21T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[Esta página fue traducida por PageTurner AI (beta). No está respaldada oficialmente por el proyecto.]]></summary>
        <content type="html"><![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>Traducción Beta No Oficial</div><div class="admonitionContent_e2NW"><p>Esta página fue traducida por <a href="https://page-turner.com/" target="_blank" rel="noopener noreferrer"><strong>PageTurner</strong></a> AI (beta). No está respaldada oficialmente por el proyecto.
¿Encontraste un error? <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=es" target="_blank" rel="noopener noreferrer">Reportar problema →</a></p></div></div>
<!-- -->
<p>tRPC ofrece una excelente experiencia de desarrollo al forzar enlaces de tipos sólidos en toda la pila gracias al poder de TypeScript. Sin desviación de contratos de API, sin generación de código.</p>
<!-- -->
<p>Desde nuestro último lanzamiento de versión mayor en agosto de 2021, la comunidad de tRPC ha experimentado un crecimiento sustancial:</p>
<ul>
<li>
<p>Ahora tenemos más de <a href="https://github.com/trpc/trpc" target="_blank" rel="noopener noreferrer">15,000 estrellas en GitHub</a></p>
</li>
<li>
<p><a href="https://trpc.io/discord" target="_blank" rel="noopener noreferrer">Una comunidad en Discord</a> con más de 2,000 miembros</p>
</li>
<li>
<p><a href="https://www.npmjs.com/package/@trpc/server" target="_blank" rel="noopener noreferrer">Más de 100k descargas semanales en npm</a></p>
</li>
<li>
<p><a href="https://github.com/trpc/trpc/graphs/contributors" target="_blank" rel="noopener noreferrer">Casi 200 colaboradores</a></p>
</li>
<li>
<p><a href="https://trpc.io/awesome" target="_blank" rel="noopener noreferrer">Un ecosistema en crecimiento de extensiones, ejemplos y contenido</a></p>
</li>
</ul>
<p><strong>Hoy lanzamos tRPC v10</strong>. Nos emociona compartir que v10 ya se usa en producción en muchos proyectos grandes de TypeScript. Este lanzamiento oficial marca su disponibilidad general para toda la comunidad.</p>
<p>Para proyectos nuevos, puedes comenzar con una <a href="https://trpc.io/awesome#-starting-points-example-projects-etc" target="_blank" rel="noopener noreferrer">aplicación de ejemplo</a> para aprender sobre tRPC v10. Para proyectos que ya usaban tRPC v9, <a href="https://trpc.io/docs/v10/migrate-from-v9-to-v10" target="_blank" rel="noopener noreferrer">visita la guía de migración a v10</a>.</p>
<h2 class="anchor anchorWithStickyNavbar_MYIC" id="resumen-de-cambios">Resumen de cambios<a href="https://trpc.io/es/blog/announcing-trpc-10#resumen-de-cambios" class="hash-link" aria-label="Enlace directo a Resumen de cambios" title="Enlace directo a Resumen de cambios">​</a></h2>
<p>v10 es el lanzamiento más importante de tRPC hasta la fecha. Es la primera vez que realizamos cambios fundamentales en su estructura, y creemos que estas modificaciones abren nuevas posibilidades para equipos ágiles que trabajan en aplicaciones de vanguardia.</p>
<h3 class="anchor anchorWithStickyNavbar_MYIC" id="experiencia-de-desarrollo-mejorada">Experiencia de desarrollo mejorada<a href="https://trpc.io/es/blog/announcing-trpc-10#experiencia-de-desarrollo-mejorada" class="hash-link" aria-label="Enlace directo a Experiencia de desarrollo mejorada" title="Enlace directo a Experiencia de desarrollo mejorada">​</a></h3>
<p>tRPC v10 abraza tu IDE. Buscamos unificar tus tipos, pero también hemos integrado tu experiencia de frontend, backend y edición en esta versión.</p>
<p>Con v10, puedes:</p>
<ul>
<li>
<p>Usar <em>"Go to Definition"</em> para saltar directamente desde tu consumidor de frontend a tu procedimiento de backend</p>
</li>
<li>
<p>Usar <em>"Rename Symbol"</em> para renombrar argumentos de entrada o procedimientos en toda tu aplicación</p>
</li>
<li>
<p><a href="https://trpc.io/docs/v10/infer-types" target="_blank" rel="noopener noreferrer">Inferir tipos más fácilmente</a> cuando quieras usar manualmente tus tipos de tRPC en la aplicación</p>
</li>
</ul>
<h3 class="anchor anchorWithStickyNavbar_MYIC" id="marco-de-backend-potente">Marco de backend potente<a href="https://trpc.io/es/blog/announcing-trpc-10#marco-de-backend-potente" class="hash-link" aria-label="Enlace directo a Marco de backend potente" title="Enlace directo a Marco de backend potente">​</a></h3>
<p>En v10, hemos rediseñado la sintaxis para definir procedimientos de backend, abriendo más oportunidades para incorporar tu lógica deseada de formas saludables. Esta versión de tRPC incluye:</p>
<ul>
<li>
<p><a href="https://trpc.io/docs/v10/middlewares" target="_blank" rel="noopener noreferrer">Middlewares reutilizables</a> con <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">Procedimientos encadenables y reutilizables</a> con capacidad para usar <a href="https://trpc.io/docs/v10/procedures#multiple-input-parsers" target="_blank" rel="noopener noreferrer">multiple input parsers</a></p>
</li>
<li>
<p><a href="https://trpc.io/docs/v10/error-handling" target="_blank" rel="noopener noreferrer">Manejo de errores</a> flexible con <a href="https://trpc.io/docs/v10/error-formatting" target="_blank" rel="noopener noreferrer">custom error formatting</a></p>
</li>
<li>
<p><a href="https://trpc.io/docs/v10/metadata" target="_blank" rel="noopener noreferrer">Metadata de procedimiento</a> para enriquecer tus procedimientos con más información</p>
</li>
</ul>
<h3 class="anchor anchorWithStickyNavbar_MYIC" id="rendimiento-de-typescript-mejorado-masivamente">Rendimiento de TypeScript mejorado masivamente<a href="https://trpc.io/es/blog/announcing-trpc-10#rendimiento-de-typescript-mejorado-masivamente" class="hash-link" aria-label="Enlace directo a Rendimiento de TypeScript mejorado masivamente" title="Enlace directo a Rendimiento de TypeScript mejorado masivamente">​</a></h3>
<p>TypeScript permite a los desarrolladores hacer cosas increíbles, pero tiene un costo. Muchas técnicas que usamos para mantener tus tipos ajustados representan una carga pesada para el compilador de TypeScript. Recibimos comentarios de la comunidad indicando que las aplicaciones más grandes usando tRPC v9 comenzaban a sufrir disminuciones de rendimiento en los IDEs debido a esta presión del compilador.</p>
<p>Nuestro objetivo es mejorar la experiencia del desarrollador para aplicaciones de todos los tamaños. En v10, hemos optimizado drásticamente el rendimiento de TypeScript (especialmente con compilación incremental de TS) para que tu editor mantenga su agilidad.</p>
<h2 class="anchor anchorWithStickyNavbar_MYIC" id="migración-incremental">Migración incremental<a href="https://trpc.io/es/blog/announcing-trpc-10#migraci%C3%B3n-incremental" class="hash-link" aria-label="Enlace directo a Migración incremental" title="Enlace directo a Migración incremental">​</a></h2>
<p>También hemos trabajado mucho para que la migración sea lo más sencilla posible, incluyendo un método <code>interop()</code> que permite (casi) total compatibilidad con routers de v9. <a href="https://trpc.io/docs/v10/migrate-from-v9-to-v10" target="_blank" rel="noopener noreferrer">Consulta la guía de migración</a> para más detalles.</p>
<p><a href="https://twitter.com/s4chinraja" target="_blank" rel="noopener noreferrer">Sachin</a> del equipo central también ha creado <a href="https://github.com/sachinraja/trpc-v10-migrate-codemod" target="_blank" rel="noopener noreferrer">un codemod</a> que puede automatizar gran parte del trabajo pesado de la migración.</p>
<h2 class="anchor anchorWithStickyNavbar_MYIC" id="un-ecosistema-en-crecimiento">Un ecosistema en crecimiento<a href="https://trpc.io/es/blog/announcing-trpc-10#un-ecosistema-en-crecimiento" class="hash-link" aria-label="Enlace directo a Un ecosistema en crecimiento" title="Enlace directo a Un ecosistema en crecimiento">​</a></h2>
<p>Continuamos desarrollando un rico conjunto de sub-bibliotecas alrededor de tRPC. Algunos ejemplos:</p>
<ul>
<li>
<p><a href="https://github.com/jlalmes/trpc-openapi" target="_blank" rel="noopener noreferrer">trpc-openapi</a> para crear fácilmente endpoints compatibles con REST</p>
</li>
<li>
<p><a href="https://github.com/t3-oss/create-t3-app" target="_blank" rel="noopener noreferrer">create-t3-app</a> para iniciar aplicaciones full-stack con Next.js y tRPC</p>
</li>
<li>
<p><a href="https://github.com/t3-oss/create-t3-turbo" target="_blank" rel="noopener noreferrer">create-t3-turbo</a> para comenzar tu próxima app React Native con tRPC</p>
</li>
<li>
<p><a href="https://github.com/jlalmes/trpc-chrome" target="_blank" rel="noopener noreferrer">trpc-chrome</a> para construir extensiones de Chrome usando tRPC</p>
</li>
<li>
<p>Adaptadores para frameworks como <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> y <a href="https://github.com/michealroberts/usetrpc" target="_blank" rel="noopener noreferrer">Vue</a></p>
</li>
</ul>
<p>Para más plugins, ejemplos y adaptadores, <a href="https://trpc.io/awesome" target="_blank" rel="noopener noreferrer">visita la colección Awesome tRPC</a>.</p>
<h2 class="anchor anchorWithStickyNavbar_MYIC" id="gracias">¡Gracias!<a href="https://trpc.io/es/blog/announcing-trpc-10#gracias" class="hash-link" aria-label="Enlace directo a ¡Gracias!" title="Enlace directo a ¡Gracias!">​</a></h2>
<p>El equipo central y yo queremos que sepas: esto recién comienza. Ya estamos experimentando con <a href="https://github.com/reactjs/rfcs/pull/229" target="_blank" rel="noopener noreferrer">React Server Components</a> y Next.js 13.</p>
<p>También quiero dar un enorme reconocimiento a <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> y <a href="https://github.com/trpc/trpc#all-contributors" target="_blank" rel="noopener noreferrer">todos los contribuidores que hicieron posible esta versión</a>.</p>
<p>Gracias por usar y apoyar tRPC.</p>
<hr>
<hr>
<h2 class="anchor anchorWithStickyNavbar_MYIC" id="slug-typescript-performance-lessonstitle-lecciones-de-rendimiento-en-typescript-durante-la-refactorización-para-v10authors-sachinraja">slug: typescript-performance-lessons
title: Lecciones de rendimiento en TypeScript durante la refactorización para v10
authors: [sachinraja]<a href="https://trpc.io/es/blog/announcing-trpc-10#slug-typescript-performance-lessonstitle-lecciones-de-rendimiento-en-typescript-durante-la-refactorizaci%C3%B3n-para-v10authors-sachinraja" class="hash-link" aria-label="Enlace directo a slug: typescript-performance-lessons
title: Lecciones de rendimiento en TypeScript durante la refactorización para v10
authors: [sachinraja]" title="Enlace directo a slug: typescript-performance-lessons
title: Lecciones de rendimiento en TypeScript durante la refactorización para v10
authors: [sachinraja]">​</a></h2>
<ul>
<li>
<p>Síguenos en Twitter: <a href="https://twitter.com/trpcio" target="_blank" rel="noopener noreferrer">@trpcio</a></p>
</li>
<li>
<p>Únete a nuestra <a href="https://trpc.io/discord" target="_blank" rel="noopener noreferrer">comunidad de Discord</a></p>
</li>
<li>
<p><a href="https://trpc.io/#try-it-out" target="_blank" rel="noopener noreferrer">Prueba tRPC en tu navegador</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">¡Conviértete en Sponsor!</span></a>]]></content>
        <author>
            <name>Alex / KATT 🐱</name>
            <uri>https://x.com/alexdotjs</uri>
        </author>
    </entry>
    <entry>
        <title type="html"><![CDATA[Presentando tRPC]]></title>
        <id>https://trpc.io/es/blog/introducing-trpc</id>
        <link href="https://trpc.io/es/blog/introducing-trpc"/>
        <updated>2021-05-05T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[Esta página fue traducida por PageTurner AI (beta). No está respaldada oficialmente por el proyecto.]]></summary>
        <content type="html"><![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>Traducción Beta No Oficial</div><div class="admonitionContent_e2NW"><p>Esta página fue traducida por <a href="https://page-turner.com/" target="_blank" rel="noopener noreferrer"><strong>PageTurner</strong></a> AI (beta). No está respaldada oficialmente por el proyecto.
¿Encontraste un error? <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=es" target="_blank" rel="noopener noreferrer">Reportar problema →</a></p></div></div>
<p>tRPC te ofrece seguridad de tipos de extremo a extremo desde tu servidor (node) hasta tu cliente, <em>sin siquiera declarar tipos</em>. Todo lo que haces en el backend es devolver datos en una función, y en el frontend usas esos datos según el nombre del endpoint.</p>
<!-- -->
<p>👋 Soy Alex, o "KATT" en GitHub, y quiero hablarte de una biblioteca llamada <a href="https://trpc.io/" target="_blank" rel="noopener noreferrer">tRPC</a>. Aún no he publicado artículos sobre ella, así que escribo esta introducción para empezar (pero ya hemos alcanzado &gt;530 🌟 en GitHub). ¡Espera artículos y videos introductorios! Si quieres mantenerte actualizado o hacer preguntas, sígueme en Twitter <a href="https://twitter.com/alexdotjs" target="_blank" rel="noopener noreferrer">@alexdotjs</a>.</p>
<p><strong>Así se ve al hacer un endpoint tRPC y una llamada desde el cliente:</strong>
<img decoding="async" loading="lazy" src="https://assets.trpc.io/www/v9/trpcgif.gif" alt="Ejemplo de llamada tRPC" class="img_Njog"></p>
<p>He creado una biblioteca para React (<code>@trpc/react</code>) que se apoya en la excelente react-query, pero la biblioteca cliente (<code>@trpc/client</code>) funciona sin React (si quieres crear una lib específica para Svelte/Vue/Angular/[..], ¡contáctame!).</p>
<p>No hay generación de código involucrada y puedes agregarlo fácilmente a tu proyecto existente de Next.js/CRA/Express.</p>
<h2 class="anchor anchorWithStickyNavbar_MYIC" id="ejemplo">Ejemplo<a href="https://trpc.io/es/blog/introducing-trpc#ejemplo" class="hash-link" aria-label="Enlace directo a Ejemplo" title="Enlace directo a Ejemplo">​</a></h2>
<p>Aquí un ejemplo de un procedimiento tRPC (también llamado endpoint) llamado <code>hello</code> que recibe un argumento <code>string</code>.</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>Y así se ve un cliente con seguridad de tipos usando esos datos:</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>¡Eso es todo lo que necesitas para tener seguridad de tipos!</strong> El <code>result</code> infiere su tipo a partir de lo que devuelve el backend. Los datos de entrada también se infieren del validador, así que los datos son seguros para usar directamente; de hecho, <em>debes</em> pasar los datos de entrada por un validador (y tRPC funciona con zod/yup/validadores personalizados desde el primer momento).</p>
<p>Aquí un enlace de CodeSandbox para probar el ejemplo: <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> (¡mira la salida del terminal en lugar de la vista previa!).</p>
<p><strong><em>¿Qué? ¿Estoy importando código del backend al cliente?</em> - No, en realidad no lo haces</strong></p>
<p>Aunque parezca que sí, no se comparte código del servidor al cliente; el <code>import type</code> de TypeScript "[...] solo importa declaraciones para anotaciones de tipos. Siempre se elimina completamente, sin dejar rastro en tiempo de ejecución." - función añadida en 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">ver documentación</a>.</p>
<p>No hay generación de código, puedes implementarlo hoy mismo siempre que tengas cómo compartir tipos entre servidor y cliente (ojalá ya uses un monorepo).</p>
<h2 class="anchor anchorWithStickyNavbar_MYIC" id="pero-solo-estamos-comenzando">¡Pero solo estamos comenzando!<a href="https://trpc.io/es/blog/introducing-trpc#pero-solo-estamos-comenzando" class="hash-link" aria-label="Enlace directo a ¡Pero solo estamos comenzando!" title="Enlace directo a ¡Pero solo estamos comenzando!">​</a></h2>
<p>Mencioné antes que existe una biblioteca para React; para usar los datos anteriores en React harías:</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>... y obtendrás datos con seguridad de tipos en el cliente.</p>
<p>Puedes agregar tRPC hoy a tu proyecto existente (tiene adaptadores para Express/Next.js), funciona con CRA y debería funcionar con React Native. Ni siquiera está atado a React, así que si quieres crear una lib para Svelte o Vue, contáctame.</p>
<h2 class="anchor anchorWithStickyNavbar_MYIC" id="qué-hay-sobre-la-mutación-de-datos">¿Qué hay sobre la mutación de datos?<a href="https://trpc.io/es/blog/introducing-trpc#qu%C3%A9-hay-sobre-la-mutaci%C3%B3n-de-datos" class="hash-link" aria-label="Enlace directo a ¿Qué hay sobre la mutación de datos?" title="Enlace directo a ¿Qué hay sobre la mutación de datos?">​</a></h2>
<p>Las mutaciones son tan simples como las consultas; en el fondo son iguales, pero se exponen diferente como azúcar sintáctico y generan peticiones HTTP POST en lugar de GET.</p>
<p>Aquí tienes un ejemplo un poco más complejo usando una base de datos, tomado de nuestro ejemplo TodoMVC en 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>Y el <strong>uso con React</strong> se ve así:</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="fin-por-ahora">Fin por ahora.<a href="https://trpc.io/es/blog/introducing-trpc#fin-por-ahora" class="hash-link" aria-label="Enlace directo a Fin por ahora." title="Enlace directo a Fin por ahora.">​</a></h2>
<p>Como dije, solo quería empezar a rodar la bola. Hay muchas más cosas:</p>
<ul>
<li>
<p>Crear contexto para solicitudes entrantes con datos específicos de usuario que se inyectan como dependencias en los resolvers - <a href="https://trpc.io/es/docs/v9/context">enlace</a></p>
</li>
<li>
<p>Soporte para middlewares en routers - <a href="https://trpc.io/es/docs/v9/middlewares">enlace</a></p>
</li>
<li>
<p>Fusión de routers (probablemente no quieras todos tus datos backend en un solo archivo) - <a href="https://trpc.io/es/docs/v9/merging-routers">enlace</a></p>
</li>
<li>
<p>El renderizado del lado del servidor más simple que hayas visto en el ecosistema React usando nuestro adaptador <code>@trpc/next</code> - <a href="https://trpc.io/es/docs/v9/">enlace</a></p>
</li>
<li>
<p>Formateo de errores con seguridad de tipos - <a href="https://trpc.io/es/docs/v9/error-formatting">enlace</a></p>
</li>
<li>
<p>Transformadores de datos (usa objetos Date/Map/Set a través del cable) - <a href="https://trpc.io/es/docs/v9/data-transformers">enlace</a></p>
</li>
<li>
<p>Utilidades para React Query</p>
</li>
</ul>
<p>Si quieres comenzar, hay varios ejemplos en la <a href="https://trpc.io/es/docs/v9/nextjs">Guía de inicio para Next.js</a>.</p>
<p><a href="https://twitter.com/alexdotjs" target="_blank" rel="noopener noreferrer">¡Sígueme en Twitter para estar al día!</a></p>]]></content>
        <author>
            <name>Alex / KATT 🐱</name>
            <uri>https://x.com/alexdotjs</uri>
        </author>
    </entry>
</feed>