tRPC là gì? Framework API end-to-end typesafe cho TypeScript

tRPC là gì? Framework API end-to-end typesafe cho TypeScript giúp loại bỏ hoàn toàn lớp API truyền thống bằng cách chia sẻ kiểu dữ liệu giữa client và server. Khác với REST hay GraphQL, tRPC không cần code generation hay build step — TypeScript tự suy luận kiểu từ server sang client ngay lập tức.

Bài viết này giải thích tRPC hoạt động thế nào, khi nào nên dùng, so sánh với REST/GraphQL và hướng dẫn setup cơ bản với Next.js.

Sơ đồ kiến trúc tRPC: client gọi procedure, server trả về, TypeScript inference tự động

tRPC hoạt động như thế nào?

tRPC (TypeScript Remote Procedure Call) dựa trên ý tưởng đơn giản: server và client chia sẻ cùng một định nghĩa kiểu TypeScript. Khi bạn định nghĩa router và procedure trên backend, TypeScript tự động “biết” hình dạng dữ liệu đầu vào/ra ở frontend mà không cần schema riêng hay codegen.

Quy trình cốt lõi:

  1. Định nghĩa router trên server với initTRPC, gồm các procedure (query, mutation, subscription).
  2. Export type AppRouter — chỉ type, không phải implementation.
  3. Client import type và tạo createTRPCClient() — autocomplete hoạt động ngay.
  4. Gọi procedure như hàm cục bộ: trpc.greeting.query({ name: 'John' }) — type-safe hoàn toàn.

Khác với GraphQL cần codegen hoặc REST cần OpenAPI spec + client generation, tRPC dùng TypeScript inference thuần túy. Không có build step, không có runtime overhead, bundle size cực nhỏ (~1KB client).

Code snippet minh họa: định nghĩa procedure với Zod validator, export router type, client gọi query với autocomplete

So sánh tRPC vs REST vs GraphQL

Tiêu chí tRPC REST GraphQL
Type safety End-to-end tự động Cần OpenAPI + codegen Cần schema + codegen
Build step Không Có (nếu gen client) Có (codegen)
Bundle size (client) ~1KB Tùy client lib ~10-50KB (Apollo/urql)
Over-fetching/under-fetching Không (gọi procedure chính xác) Phổ biến Giải quyết được
Caching React Query built-in HTTP cache chuẩn Normalized cache phức tạp
Phù hợp Monorepo, full-stack TS, team nhỏ Public API, multi-client Public API, query phức tạp

tRPC tỏa sáng khi bạn kiểm soát cả client và server (monorepo Next.js, Expo, SvelteKit). Nếu API public cho third-party, REST/GraphQL vẫn phù hợp hơn.

Setup tRPC với Next.js App Router

Cài đặt:

npm install @trpc/server @trpc/client @trpc/react-query @trpc/next superjson zod @tanstack/react-query

Tạo server/trpc.ts (init tRPC):

import { initTRPC, TRPCError } from '@trpc/server';
import { ZodError } from 'zod';
import { getServerSession } from 'next-auth';
import { authOptions } from '@/lib/auth';
import SuperJSON from 'superjson';

export const createTRPCContext = async () => {
  const session = await getServerSession(authOptions);
  return { session };
};

const t = initTRPC.context().create({
  transformer: SuperJSON,
  errorFormatter: ({ shape, error }) => ({
    ...shape,
    data: { ...shape.data, zodError: error.cause instanceof ZodError ? error.cause.flatten() : null },
  }),
});

export const createTRPCRouter = t.router;
export const publicProcedure = t.procedure;
export const protectedProcedure = t.procedure.use(async ({ ctx, next }) => {
  if (!ctx.session) throw new TRPCError({ code: 'UNAUTHORIZED' });
  return next({ ctx: { session: ctx.session } });
});

Tạo router server/routers/app.ts:

import { z } from 'zod';
import { createTRPCRouter, publicProcedure, protectedProcedure } from '@/server/trpc';

export const appRouter = createTRPCRouter({
  greeting: publicProcedure
    .input(z.object({ name: z.string() }))
    .query(({ input }) => `Hello, ${input.name}!`),

  getProfile: protectedProcedure.query(async ({ ctx }) => {
    // ctx.session đã có user
    return { id: ctx.session.user.id, name: ctx.session.user.name };
  }),

  updateName: protectedProcedure
    .input(z.object({ name: z.string().min(2) }))
    .mutation(async ({ ctx, input }) => {
      // update DB
      return { success: true };
    }),
});

export type AppRouter = typeof appRouter;

Tạo Next.js handler app/api/trpc/[trpc]/route.ts:

import { fetchRequestHandler } from '@trpc/server/adapters/fetch';
import { appRouter } from '@/server/routers/app';
import { createTRPCContext } from '@/server/trpc';

const handler = (req: Request) =>
  fetchRequestHandler({
    endpoint: '/api/trpc',
    req,
    router: appRouter,
    createContext: createTRPCContext,
  });

export { handler as GET, handler as POST };

Client provider components/trpc-provider.tsx:

'use client';
import { createTRPCReact } from '@trpc/react-query';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { httpBatchLink } from '@trpc/client';
import SuperJSON from 'superjson';
import { useState } from 'react';
import type { AppRouter } from '@/server/routers/app';

const trpc = createTRPCReact();

export function TRPCProvider({ children }: { children: React.ReactNode }) {
  const [queryClient] = useState(() => new QueryClient({
    defaultOptions: { queries: { staleTime: 60_000 } },
  }));
  const [trpcClient] = useState(() => trpc.createClient({
    links: [httpBatchLink({ url: '/api/trpc', transformer: SuperJSON })],
  }));

  return (
    
      
        {children}
      
    
  );
}

Dùng trong component:

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

export function Greeting() {
  const { data } = trpc.greeting.useQuery({ name: 'tRPC' });
  return 
{data ?? 'Loading...'}
; }

Khi nào nên chọn tRPC?

  • ✓ Dùng tRPC khi: Full-stack TypeScript, monorepo, team nhỏ-trung bình, muốn DX tối ưu, ít boilerplate.
  • ✗ Tránh tRPC khi: API public cho third-party, nhiều client ngôn ngữ khác nhau, cần caching phức tạp như GraphQL, team đã quen REST/GraphQL và không muốn chuyển.

Hệ sinh thái và công cụ hỗ trợ

tRPC đang trở thành lựa chọn mặc định cho các dự án full-stack TypeScript hiện đại nhờ DX vượt trội và zero-overhead. Nếu bạn đang xây dựng ứng dụng Next.js/React/Expo với TypeScript, hãy thử tRPC — nó có thể loại bỏ hoàn toàn các file OpenAPI, codegen script và client wrapper thủ công.

Tôi là một lập trình viên IOS. Code chính là IOS nhưng thỉnnh thoảng vẫn đá sang Android hoặc web. Mặc dù không quá thông thạo nhưng tôi sẽ chia sẻ những kiến thức mà mình đã tìm hiểu, áp dụng qua.

Bài viết liên quan

OpenTelemetry là gì? Tiêu chuẩn observability cho microservices hiện đại

OpenTelemetry là gì? Tiêu chuẩn observability cho microservices hiện đại OpenTelemetry (OTel) là bộ công cụ mã nguồn mở thuộc CNCF (Cloud Native Computing Foundation) cung cấp tiêu chuẩn thống…

Xem thêm

GraphQL là gì? So sánh với REST API và ứng dụng thực tế

GraphQL là một ngôn ngữ truy vấn API và môi trường runtime được phát triển bởi Facebook (Meta) năm 2012 và công khai nguồn mở vào năm 2015. Nó cho…

Xem thêm

MCP (Model Context Protocol) là gì? Chuẩn kết nối AI với công cụ

MCP (Model Context Protocol) là chuẩn mở kết nối ứng dụng AI (Claude, ChatGPT, Cursor, VS Code…) với nguồn dữ liệu bên ngoài — file cục bộ, cơ sở dữ…

Xem thêm
0 0 đánh giá
Article Rating
Theo dõi
Thông báo của
guest
0 Comments
Cũ nhất
Mới nhất Được bỏ phiếu nhiều nhất