Skip to main content

Guía de tRPC

Guía para trabajar con tRPC en InsureHero: API interna del dashboard y jobs, distinta de la API Shield (REST pública). Si expones funcionalidad a partners o canales externos, revisa Nuevas rutas Shield y API Reference: tRPC.

Convenciones generales del repo: Estructura base y extensión.

Crear un Router

Estructura Básica

// src/trpc/example.router.ts
import { router, protectedProcedure } from './trpc';
import { z } from 'zod';

export const exampleRouter = router({
// Query
list: protectedProcedure
.input(z.object({
page: z.number().default(1),
limit: z.number().default(10)
}))
.query(async ({ input, ctx }) => {
// Lógica de query
return { data: [], total: 0 };
}),

// Mutation
create: protectedProcedure
.input(z.object({
name: z.string().min(1),
description: z.string().optional()
}))
.mutation(async ({ input, ctx }) => {
// Lógica de creación
return { id: 'new-id', ...input };
})
});

Registrar Router

// src/trpc/index.ts
import { exampleRouter } from './example.router';

export const appRouter = router({
example: exampleRouter,
// otros routers...
});

Procedimientos

Public Procedure

Acceso sin autenticación:

import { publicProcedure } from './trpc';

export const publicRouter = router({
publicEndpoint: publicProcedure.query(() => {
return { message: 'Public data' };
})
});

Protected Procedure

Requiere autenticación:

import { protectedProcedure } from './trpc';

export const protectedRouter = router({
protectedEndpoint: protectedProcedure.query(async ({ ctx }) => {
// ctx.user está disponible
return { user: ctx.user };
})
});

Validación con Zod

import { z } from 'zod';

const createSchema = z.object({
name: z.string().min(1, 'Name is required'),
email: z.string().email('Invalid email'),
age: z.number().min(18).max(100)
});

create: protectedProcedure
.input(createSchema)
.mutation(async ({ input }) => {
// input está tipado correctamente
})

Usar en el Cliente

Query

import { trpc } from '@/trpc/client';

const { data, isLoading, error } = trpc.example.list.useQuery({
page: 1,
limit: 10
});

Mutation

const createMutation = trpc.example.create.useMutation({
onSuccess: (data) => {
console.log('Created:', data);
},
onError: (error) => {
console.error('Error:', error);
}
});

// Usar
createMutation.mutate({
name: 'Example',
description: 'Description'
});

Optimistic Updates

const utils = trpc.useUtils();

const updateMutation = trpc.example.update.useMutation({
onMutate: async (newData) => {
// Cancelar queries en curso
await utils.example.get.cancel({ id: newData.id });

// Snapshot del valor anterior
const previous = utils.example.get.getData({ id: newData.id });

// Optimistic update
utils.example.get.setData({ id: newData.id }, newData);

return { previous };
},
onError: (err, newData, context) => {
// Revertir en caso de error
utils.example.get.setData(
{ id: newData.id },
context.previous
);
},
onSettled: () => {
// Refetch
utils.example.get.invalidate();
}
});

Error Handling

const { data, error } = trpc.example.get.useQuery(
{ id: '123' },
{
retry: 3,
retryDelay: 1000,
onError: (error) => {
// Manejar error
if (error.data?.code === 'UNAUTHORIZED') {
// Redirigir a login
}
}
}
);