Saltar al contenido principal

Presentando tRPC

· 5 min de lectura
Alex / KATT 🐱
Creator of tRPC
Traducción Beta No Oficial

Esta página fue traducida por PageTurner AI (beta). No está respaldada oficialmente por el proyecto. ¿Encontraste un error? Reportar problema →

tRPC te ofrece seguridad de tipos de extremo a extremo desde tu servidor (node) hasta tu cliente, sin siquiera declarar tipos. 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.

👋 Soy Alex, o "KATT" en GitHub, y quiero hablarte de una biblioteca llamada tRPC. Aún no he publicado artículos sobre ella, así que escribo esta introducción para empezar (pero ya hemos alcanzado >530 🌟 en GitHub). ¡Espera artículos y videos introductorios! Si quieres mantenerte actualizado o hacer preguntas, sígueme en Twitter @alexdotjs.

Así se ve al hacer un endpoint tRPC y una llamada desde el cliente: Ejemplo de llamada tRPC

He creado una biblioteca para React (@trpc/react) que se apoya en la excelente react-query, pero la biblioteca cliente (@trpc/client) funciona sin React (si quieres crear una lib específica para Svelte/Vue/Angular/[..], ¡contáctame!).

No hay generación de código involucrada y puedes agregarlo fácilmente a tu proyecto existente de Next.js/CRA/Express.

Ejemplo

Aquí un ejemplo de un procedimiento tRPC (también llamado endpoint) llamado hello que recibe un argumento string.

tsx
const appRouter = trpc.router().query('hello', {
input: z.string().optional(),
resolve: ({ input }) => {
return {
text: `hello ${input ?? 'world'}`,
};
},
});
export type AppRouter = typeof appRouter;
tsx
const appRouter = trpc.router().query('hello', {
input: z.string().optional(),
resolve: ({ input }) => {
return {
text: `hello ${input ?? 'world'}`,
};
},
});
export type AppRouter = typeof appRouter;

Y así se ve un cliente con seguridad de tipos usando esos datos:

tsx
import type { AppRouter } from './server';
async function main() {
const client = createTRPCClient<AppRouter>({
url: `http://localhost:2022`,
});
const result = await client.query('hello', '@alexdotjs');
console.log(result); // --> { text: "hello @alexdotjs" }
}
main();
tsx
import type { AppRouter } from './server';
async function main() {
const client = createTRPCClient<AppRouter>({
url: `http://localhost:2022`,
});
const result = await client.query('hello', '@alexdotjs');
console.log(result); // --> { text: "hello @alexdotjs" }
}
main();

¡Eso es todo lo que necesitas para tener seguridad de tipos! El result 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, debes pasar los datos de entrada por un validador (y tRPC funciona con zod/yup/validadores personalizados desde el primer momento).

Aquí un enlace de CodeSandbox para probar el ejemplo: https://githubbox.com/trpc/trpc/tree/main/examples/standalone-server (¡mira la salida del terminal en lugar de la vista previa!).

¿Qué? ¿Estoy importando código del backend al cliente? - No, en realidad no lo haces

Aunque parezca que sí, no se comparte código del servidor al cliente; el import type 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 - ver documentación.

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).

¡Pero solo estamos comenzando!

Mencioné antes que existe una biblioteca para React; para usar los datos anteriores en React harías:

tsx
const { data } = trpc.useQuery(['hello', '@alexdotjs']);
tsx
const { data } = trpc.useQuery(['hello', '@alexdotjs']);

... y obtendrás datos con seguridad de tipos en el cliente.

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.

¿Qué hay sobre la mutación de datos?

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.

Aquí tienes un ejemplo un poco más complejo usando una base de datos, tomado de nuestro ejemplo TodoMVC en todomvc.trpc.io / https://github.com/trpc/trpc/tree/main/examples/next-prisma-todomvc

tsx
const todoRouter = createRouter().mutation('add', {
input: z.object({
id: z.string().uuid(),
data: z.object({
completed: z.boolean().optional(),
text: z.string().min(1).optional(),
}),
}),
async resolve({ ctx, input }) {
const { id, data } = input;
const todo = await ctx.task.update({
where: { id },
data,
});
return todo;
},
});
tsx
const todoRouter = createRouter().mutation('add', {
input: z.object({
id: z.string().uuid(),
data: z.object({
completed: z.boolean().optional(),
text: z.string().min(1).optional(),
}),
}),
async resolve({ ctx, input }) {
const { id, data } = input;
const todo = await ctx.task.update({
where: { id },
data,
});
return todo;
},
});

Y el uso con React se ve así:

tsx
const addTask = trpc.useMutation('todos.add');
return (
<>
<input
placeholder="What needs to be done?"
onKeyDown={(e) => {
const text = e.currentTarget.value.trim();
if (e.key === 'Enter' && text) {
addTask.mutate({ text });
e.currentTarget.value = '';
}
}}
/>
</>
)
tsx
const addTask = trpc.useMutation('todos.add');
return (
<>
<input
placeholder="What needs to be done?"
onKeyDown={(e) => {
const text = e.currentTarget.value.trim();
if (e.key === 'Enter' && text) {
addTask.mutate({ text });
e.currentTarget.value = '';
}
}}
/>
</>
)

Fin por ahora.

Como dije, solo quería empezar a rodar la bola. Hay muchas más cosas:

  • Crear contexto para solicitudes entrantes con datos específicos de usuario que se inyectan como dependencias en los resolvers - enlace

  • Soporte para middlewares en routers - enlace

  • Fusión de routers (probablemente no quieras todos tus datos backend en un solo archivo) - enlace

  • El renderizado del lado del servidor más simple que hayas visto en el ecosistema React usando nuestro adaptador @trpc/next - enlace

  • Formateo de errores con seguridad de tipos - enlace

  • Transformadores de datos (usa objetos Date/Map/Set a través del cable) - enlace

  • Utilidades para React Query

Si quieres comenzar, hay varios ejemplos en la Guía de inicio para Next.js.

¡Sígueme en Twitter para estar al día!