Структурирование с помощью маршрутизаторов tRPC
Научитесь организовывать API в логические модули с помощью маршрутизаторов tRPC и эффективно объединять их.
«Структурирование с помощью маршрутизаторов tRPC» — бесплатный урок tRPC End-to-End Type Safe APIs на CoddyKit. Это урок 1 из 4. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения tRPC End-to-End Type Safe APIs, и твой прогресс синхронизируется между веб-версией и приложением CoddyKit. Курс tRPC End-to-End Type Safe APIs содержит 4 уроков всего.
Части этого урока еще не переведены и отображаются на английском.
Organize Your API with Routers
As your application grows, your API can become messy. tRPC routers help you organize your API into logical, reusable modules.
Think of a router as a folder for your API endpoints. Instead of one giant file, you group related operations together.
- Modularity: Break down complex APIs.
- Readability: Easier to understand specific parts.
- Scalability: Simplifies adding new features.
Defining Your First Router
In tRPC, you create a router using trpc.router(). This function comes from your tRPC instance, which we assume is set up.
A router acts as a container for your API procedures (queries and mutations). Each router can have its own set of procedures.
Here's how you might define a simple "user" router:
const userRouter = t.router({
// Procedures will go here
});Procedures Inside Routers
Once you have a router, you can add procedures to it. Procedures are the actual API endpoints that perform actions like fetching data (queries) or modifying data (mutations).
You attach procedures using methods like .query() or .mutation() directly to your router instance. For example, a user router might have a getById query.
const userRouter = t.router({
getById: t.procedure
.input(z.string())
.query(({ input }) => {
return { id: input, name: `User ${input}` };
}),
});Simple User Router in Action
Let's see a basic tRPC router for users. This example includes a minimal setup to make it runnable, focusing on the router definition.
The appRouter is our main router that will contain all others. Here, we're just setting up a single userRouter with a simple query.
// src/server/trpc.ts (simplified for example)
import { initTRPC } from '@trpc/server';
import { createExpressMiddleware } from '@trpc/server/adapters/express';
import express from 'express';
const t = initTRPC.create();
// Define a user router
const userRouter = t.router({
getById: t.procedure
.input((val: unknown) => {
if (typeof val === 'string') return val;
throw new Error('Input must be a string');
})
.query(({ input }) => {
// In a real app, you'd fetch from a database
return { id: input, name: `User ${input}` };
}),
});
// The main app router that will merge all sub-routers
const appRouter = t.router({
user: userRouter, // Attach the user router
});
// Export type definition for client-side
export type AppRouter = typeof appRouter;
// --- Express Server Setup (for runnable example) ---
const app = express();
app.use(
'/trpc',
createExpressMiddleware({
router: appRouter,
createContext: () => ({}),
})
);
const server = app.listen(3000, () => {
console.log('tRPC server listening on http://localhost:3000/trpc');
console.log('Try visiting: http://localhost:3000/trpc/user.getById?input="123"');
});
// To stop the server and exit gracefully after a short delay
setTimeout(() => {
console.log('Server shutting down.');
server.close(() => {
process.exit(0);
});
}, 5000);Scaling with Multiple Routers
Imagine an API for an e-commerce site. You might have operations for users, products, orders, and payments.
Putting all these into a single router would make it huge and hard to manage. Instead, you create a separate router for each logical domain:
userRouterfor user-related tasks.productRouterfor product management.orderRouterfor order processing.
This keeps your codebase tidy and promotes better teamwork.
Combining Routers with Merge
Once you have multiple individual routers, you need a way to combine them into a single, unified API that your frontend can interact with. This is where router.merge() comes in.
You use .merge() on your main, "root" router to include all your sub-routers. This makes all procedures from the sub-routers available under a specific namespace, defined by the key you assign them to.
Merging User & Post Routers
Let's expand our example to include a postRouter and then merge it with our userRouter into a single appRouter.
Notice how we attach each sub-router to a key (e.g., user: userRouter), creating a nested structure for our API calls.
// src/server/trpc.ts (simplified for example)
import { initTRPC } from '@trpc/server';
import { createExpressMiddleware } from '@trpc/server/adapters/express';
import express from 'express';
const t = initTRPC.create();
// Define a user router
const userRouter = t.router({
getById: t.procedure
.input((val: unknown) => {
if (typeof val === 'string') return val;
throw new Error('Input must be a string');
})
.query(({ input }) => {
return { id: input, name: `User ${input}` };
}),
});
// Define a post router
const postRouter = t.router({
getAll: t.procedure
.query(() => {
return [{ id: 'p1', title: 'First Post' }, { id: 'p2', title: 'Second Post' }];
}),
create: t.procedure
.input((val: unknown) => {
if (typeof val === 'object' && val !== null && 'title' in val && typeof val.title === 'string') return val as { title: string };
throw new Error('Input must be an object with a title string');
})
.mutation(({ input }) => {
return { id: `new-${Date.now()}`, title: input.title };
}),
});
// The main app router that merges all sub-routers
const appRouter = t.router({
user: userRouter, // Attach user router under 'user' namespace
post: postRouter, // Attach post router under 'post' namespace
});
export type AppRouter = typeof appRouter;
// --- Express Server Setup (for runnable example) ---
const app = express();
app.use(express.json()); // For handling mutation inputs
app.use(
'/trpc',
createExpressMiddleware({
router: appRouter,
createContext: () => ({}),
})
);
const server = app.listen(3000, () => {
console.log('tRPC server listening on http://localhost:3000/trpc');
console.log('Available endpoints:');
console.log(' GET /trpc/user.getById?input="456"');
console.log(' GET /trpc/post.getAll');
console.log(' POST /trpc/post.create (body: {"title": "New Post"})');
});
setTimeout(() => {
console.log('Server shutting down.');
server.close(() => {
process.exit(0);
});
}, 5000);Accessing Merged Procedures
Once routers are merged, your frontend client can access procedures using dot notation, reflecting the nested structure you defined.
- To call a user query:
client.user.getById(...) - To call a post query:
client.post.getAll() - To call a post mutation:
client.post.create(...)
This clear naming convention makes your API intuitive and easy to navigate.
Organizing Your Router Files
For larger projects, it's common to place each router in its own file. This keeps your codebase modular and easy to navigate.
A typical structure might look like this:
src/server/trpc.ts(tRPC instance, root router definition)src/server/routers/user.ts(userRouterdefinition)src/server/routers/post.ts(postRouterdefinition)src/server/index.ts(server setup, merging routers)
Router Merging Check
You have a productRouter and an orderRouter. You want to combine them into your main appRouter so they can be accessed as client.product... and client.order....
Which code snippet correctly merges these routers?
Recap & Next Steps
You've learned how tRPC routers help organize your API into logical, manageable modules. By defining individual routers for different domains and then merging them into a root router, you create a scalable and maintainable API structure.
t.router(): Creates a new router.- Procedures: Added to routers using
.query()or.mutation(). - Merging: Combine routers into a single API by assigning them to keys in the root router definition.
Next, you'll dive deeper into implementing specific query procedures!
Часто задаваемые вопросы
Урок «Структурирование с помощью маршрутизаторов tRPC» бесплатный?
Да — полный текст урока «Структурирование с помощью маршрутизаторов tRPC» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс tRPC End-to-End Type Safe APIs, подпишись на CoddyKit PRO. Курс tRPC End-to-End Type Safe APIs содержит 4 уроков всего.
Чему я научусь в уроке «Структурирование с помощью маршрутизаторов tRPC»?
Научитесь организовывать API в логические модули с помощью маршрутизаторов tRPC и эффективно объединять их. Ты практикуешь tRPC End-to-End Type Safe APIs с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.
Нужен ли мне опыт, чтобы начать tRPC End-to-End Type Safe APIs?
Предыдущий опыт не требуется. tRPC End-to-End Type Safe APIs на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 1 из 4.
Сколько времени занимает урок «Структурирование с помощью маршрутизаторов tRPC»?
Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.
Можно ли писать и запускать код в этом уроке tRPC End-to-End Type Safe APIs?
Да. Каждый урок tRPC End-to-End Type Safe APIs включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.
Все уроки этого курса
- Структурирование с помощью маршрутизаторов tRPC
- Реализация процедур запросов
- Разработка процедур мутаций
- Объединение и вложенность маршрутизаторов