Aller au contenu principal
Version : 11.x

Types de contenu

Traduction Bêta Non Officielle

Cette page a été traduite par PageTurner AI (bêta). Non approuvée officiellement par le projet. Vous avez trouvé une erreur ? Signaler un problème →

tRPC prend en charge plusieurs types de contenu comme entrées de procédure : données sérialisables en JSON, FormData, File, Blob et autres types binaires.

JSON (par défaut)

Par défaut, tRPC envoie et reçoit des données sérialisables en JSON. Aucune configuration supplémentaire n'est nécessaire — toute entrée pouvant être sérialisée en JSON fonctionne immédiatement avec tous les liens (httpLink, httpBatchLink, httpBatchStreamLink).

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

Types de contenu non-JSON

En complément du JSON, tRPC peut utiliser FormData, File et d'autres types binaires comme entrées de procédure.

Configuration client

info

Bien que tRPC prenne nativement en charge plusieurs types non sérialisables en JSON, votre client peut nécessiter une légère configuration des liens selon votre installation.

httpLink prend en charge les types de contenu non-JSON immédiatement — si vous utilisez uniquement ce lien, votre configuration existante devrait fonctionner sans modification.

ts
import { createTRPCClient, httpLink } from '@trpc/client';
import type { AppRouter } from './server';
 
createTRPCClient<AppRouter>({
links: [
httpLink({
url: 'http://localhost:2022',
}),
],
});
ts
import { createTRPCClient, httpLink } from '@trpc/client';
import type { AppRouter } from './server';
 
createTRPCClient<AppRouter>({
links: [
httpLink({
url: 'http://localhost:2022',
}),
],
});

Cependant, tous les liens ne prennent pas en charge ces types de contenu. Si vous utilisez httpBatchLink ou httpBatchStreamLink, vous devrez inclure un splitLink et router les requêtes selon le type de contenu.

ts
import {
createTRPCClient,
httpBatchLink,
httpLink,
isNonJsonSerializable,
splitLink,
} from '@trpc/client';
import type { AppRouter } from './server';
 
const url = 'http://localhost:2022';
 
createTRPCClient<AppRouter>({
links: [
splitLink({
condition: (op) => isNonJsonSerializable(op.input),
true: httpLink({
url,
}),
false: httpBatchLink({
url,
}),
}),
],
});
ts
import {
createTRPCClient,
httpBatchLink,
httpLink,
isNonJsonSerializable,
splitLink,
} from '@trpc/client';
import type { AppRouter } from './server';
 
const url = 'http://localhost:2022';
 
createTRPCClient<AppRouter>({
links: [
splitLink({
condition: (op) => isNonJsonSerializable(op.input),
true: httpLink({
url,
}),
false: httpBatchLink({
url,
}),
}),
],
});

Si vous utilisez transformer dans votre serveur tRPC, TypeScript exige que le lien de votre client tRPC définisse également transformer. Utilisez cet exemple comme base :

ts
import {
createTRPCClient,
httpBatchLink,
httpLink,
isNonJsonSerializable,
splitLink,
} from '@trpc/client';
import superjson from 'superjson';
import type { AppRouter } from './server';
 
const url = 'http://localhost:2022';
 
createTRPCClient<AppRouter>({
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: (data) => superjson.deserialize(data), // or your other transformer
},
}),
false: httpBatchLink({
url,
transformer: superjson, // or your other transformer
}),
}),
],
});
ts
import {
createTRPCClient,
httpBatchLink,
httpLink,
isNonJsonSerializable,
splitLink,
} from '@trpc/client';
import superjson from 'superjson';
import type { AppRouter } from './server';
 
const url = 'http://localhost:2022';
 
createTRPCClient<AppRouter>({
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: (data) => superjson.deserialize(data), // or your other transformer
},
}),
false: httpBatchLink({
url,
transformer: superjson, // or your other transformer
}),
}),
],
});

Configuration serveur

info

Lorsqu'une requête est traitée par tRPC, il analyse le corps de la requête en fonction de l'en-tête Content-Type.
Si vous rencontrez des erreurs comme Failed to parse body as XXX, assurez-vous que votre serveur (ex : Express, Next.js) n'analyse pas le corps de la requête avant que tRPC ne le traite.

ts
// Example in express
import express from 'express';
import * as trpcExpress from '@trpc/server/adapters/express';
import { appRouter } from './router';
 
// incorrect
const app1 = express();
app1.use(express.json()); // this tries to parse body before tRPC.
app1.post('/express/hello', (req, res) => { res.end(); }); // normal express route handler
app1.use('/trpc', trpcExpress.createExpressMiddleware({ router: appRouter })); // tRPC fails to parse body
 
// correct
const app2 = express();
app2.use('/express', express.json()); // do it only in "/express/*" path
app2.post('/express/hello', (req, res) => { res.end(); });
app2.use('/trpc', trpcExpress.createExpressMiddleware({ router: appRouter })); // tRPC can parse body
ts
// Example in express
import express from 'express';
import * as trpcExpress from '@trpc/server/adapters/express';
import { appRouter } from './router';
 
// incorrect
const app1 = express();
app1.use(express.json()); // this tries to parse body before tRPC.
app1.post('/express/hello', (req, res) => { res.end(); }); // normal express route handler
app1.use('/trpc', trpcExpress.createExpressMiddleware({ router: appRouter })); // tRPC fails to parse body
 
// correct
const app2 = express();
app2.use('/express', express.json()); // do it only in "/express/*" path
app2.post('/express/hello', (req, res) => { res.end(); });
app2.use('/trpc', trpcExpress.createExpressMiddleware({ router: appRouter })); // tRPC can parse body

Entrée FormData

FormData est pris en charge nativement. Pour des usages avancés, vous pouvez le combiner avec une bibliothèque comme zod-form-data pour valider les entrées de manière typée.

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')}`,
};
}),
});

Pour un exemple de code plus avancé, consultez notre projet d'exemple ici

Entrées File et autres types binaires

tRPC convertit de nombreux types de contenu octet en ReadableStream utilisable dans une procédure. Actuellement, cela inclut Blob, Uint8Array et 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,
};
}),
});