Saltar al contenido principal
Versión: 11.x

Tipos de Contenido No JSON

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 →

Además de datos serializables en JSON, tRPC puede usar FormData, File y otros tipos binarios como entradas para procedimientos.

Configuración del Cliente

información

Aunque tRPC soporta nativamente varios tipos no serializables en JSON, tu cliente podría necesitar una pequeña configuración de links para admitirlos dependiendo de tu configuración.

httpLink soporta tipos de contenido no JSON directamente. Si solo usas este, tu configuración existente funcionará inmediatamente.

ts
import { httpLink } from '@trpc/client';
trpc.createClient({
links: [
httpLink({
url: 'http://localhost:2022',
}),
],
});
ts
import { httpLink } from '@trpc/client';
trpc.createClient({
links: [
httpLink({
url: 'http://localhost:2022',
}),
],
});

Sin embargo, no todos los links soportan estos nuevos tipos de contenido. Si usas httpBatchLink o httpBatchStreamLink, necesitarás incluir un splitLink y decidir qué link usar según el contenido.

ts
import {
httpBatchLink,
httpLink,
isNonJsonSerializable,
splitLink,
} from '@trpc/client';
trpc.createClient({
links: [
splitLink({
condition: (op) => isNonJsonSerializable(op.input),
true: httpLink({
url,
}),
false: httpBatchLink({
url,
}),
}),
],
});
ts
import {
httpBatchLink,
httpLink,
isNonJsonSerializable,
splitLink,
} from '@trpc/client';
trpc.createClient({
links: [
splitLink({
condition: (op) => isNonJsonSerializable(op.input),
true: httpLink({
url,
}),
false: httpBatchLink({
url,
}),
}),
],
});

Si usas transformer en tu servidor tRPC, TypeScript requiere que tu link del cliente tRPC también defina transformer.
Usa este ejemplo como base:

ts
import {
httpBatchLink,
httpLink,
isNonJsonSerializable,
splitLink,
} from '@trpc/client';
import superjson from 'superjson';
trpc.createClient({
links: [
splitLink({
condition: (op) => isNonJsonSerializable(op.input),
true: httpLink({
url,
transformer: {
// request - convert data before sending to the tRPC server
serialize: (data) => data,
// response - convert the tRPC response before using it in client
deserialize: superjson.deserialize, // or your other transformer
},
}),
false: httpBatchLink({
url,
transformers: superjson, // or your other transformer
}),
}),
],
});
ts
import {
httpBatchLink,
httpLink,
isNonJsonSerializable,
splitLink,
} from '@trpc/client';
import superjson from 'superjson';
trpc.createClient({
links: [
splitLink({
condition: (op) => isNonJsonSerializable(op.input),
true: httpLink({
url,
transformer: {
// request - convert data before sending to the tRPC server
serialize: (data) => data,
// response - convert the tRPC response before using it in client
deserialize: superjson.deserialize, // or your other transformer
},
}),
false: httpBatchLink({
url,
transformers: superjson, // or your other transformer
}),
}),
],
});

Uso en el Servidor

información

Cuando tRPC maneja una solicitud, se encarga de analizar el cuerpo de la misma basándose en el encabezado Content-Type.
Si encuentras errores como Failed to parse body as XXX, asegúrate de que tu servidor (por ejemplo, Express, Next.js) no esté analizando el cuerpo antes de que tRPC lo maneje.

ts
// Example in express
// incorrect
const app = express();
app.use(express.json()); // this try to parse body before tRPC.
app.post('/express/hello', (req,res) => {/* ... */ }); // normal express route handler
app.use('/trpc', trpcExpress.createExpressMiddleware({ /* ... */}))// tRPC fails to parse body
// correct
const app = express();
app.use('/express', express.json()); // do it only in "/express/*" path
app.post('/express/hello', (req,res) => {/* ... */ });
app.use('/trpc', trpcExpress.createExpressMiddleware({ /* ... */}))// tRPC can parse body
ts
// Example in express
// incorrect
const app = express();
app.use(express.json()); // this try to parse body before tRPC.
app.post('/express/hello', (req,res) => {/* ... */ }); // normal express route handler
app.use('/trpc', trpcExpress.createExpressMiddleware({ /* ... */}))// tRPC fails to parse body
// correct
const app = express();
app.use('/express', express.json()); // do it only in "/express/*" path
app.post('/express/hello', (req,res) => {/* ... */ });
app.use('/trpc', trpcExpress.createExpressMiddleware({ /* ... */}))// tRPC can parse body

Entrada FormData

FormData tiene soporte nativo. Para usos más avanzados, puedes combinarlo con bibliotecas como zod-form-data para validar entradas de forma tipada.

ts
import { z } from 'zod';
 
export const t = initTRPC.create();
const publicProcedure = t.procedure;
 
export const appRouter = t.router({
hello: publicProcedure.input(z.instanceof(FormData)).mutation((opts) => {
const data = opts.input;
const data: FormData
return {
greeting: `Hello ${data.get('name')}`,
};
}),
});
ts
import { z } from 'zod';
 
export const t = initTRPC.create();
const publicProcedure = t.procedure;
 
export const appRouter = t.router({
hello: publicProcedure.input(z.instanceof(FormData)).mutation((opts) => {
const data = opts.input;
const data: FormData
return {
greeting: `Hello ${data.get('name')}`,
};
}),
});

Para un ejemplo de código más avanzado, consulta nuestro proyecto de ejemplo aquí

Entradas File y Otros Tipos Binarios

tRPC convierte muchos tipos de contenido octet a un ReadableStream que puede consumirse en un procedimiento. Actualmente estos incluyen Blob, Uint8Array y File.

ts
import { octetInputParser } from '@trpc/server/http';
 
export const t = initTRPC.create();
const publicProcedure = t.procedure;
 
export const appRouter = t.router({
upload: publicProcedure.input(octetInputParser).mutation((opts) => {
const data = opts.input;
const data: ReadableStream<any>
return {
valid: true,
};
}),
});
ts
import { octetInputParser } from '@trpc/server/http';
 
export const t = initTRPC.create();
const publicProcedure = t.procedure;
 
export const appRouter = t.router({
upload: publicProcedure.input(octetInputParser).mutation((opts) => {
const data = opts.input;
const data: ReadableStream<any>
return {
valid: true,
};
}),
});