API Endpoints (tRPC)
Relevant source files
The following files were used as context for generating this wiki page:The application uses tRPC (TypeScript Remote Procedure Call) as its primary API layer for communication between the React Native client and the Express.js backend. This approach provides end-to-end type safety, ensuring that the client and server are always in sync regarding API contracts, data shapes, and error handling. All tRPC endpoints are exposed under the /api/trpc path on the server.
The tRPC setup includes client-side hooks for data fetching via @tanstack/react-query and a server-side router that handles incoming requests, authentication, and business logic. Data serialization is managed by superjson to allow for the transfer of complex data types like Date objects without manual transformation.
Architecture Overview
The tRPC architecture consists of a client that makes procedure calls and a server that hosts the corresponding routers and procedures. The communication happens over standard HTTP, making it compatible with the existing Express server.
This diagram illustrates the flow of a tRPC request from the mobile client to the server and back.
Sources: lib/trpc.ts, server/_core/trpc.ts
Server-Side Implementation
The server-side of tRPC is built on an Express.js server. It defines the API procedures, handles authentication via middleware, and structures the API using routers.
Sources: server/_core/trpc.ts, server/_core/sdk.ts
Initialization and Context
The tRPC instance is initialized in server/_core/trpc.ts. It uses superjson as the data transformer to ensure type fidelity between the client and server. The context for each request is created to include the authenticated user, which is determined by validating a session cookie or a Bearer token.
The authenticateRequest function from the SDK is used to verify the session and retrieve user data. This user object is then made available in the tRPC context for all procedures.
// server/_core/trpc.ts:7-10
const t = initTRPC.context<TrpcContext>().create({
transformer: superjson,
});
Sources: server/_core/trpc.ts:7-10, server/_core/sdk.ts:200-238
Procedures and Middleware
The application defines three types of procedures, each with different access levels controlled by middleware.
| Procedure Type | Description | Middleware |
|---|---|---|
publicProcedure | Accessible by any client, authenticated or not. No middleware applied. | None |
protectedProcedure | Requires an authenticated user. The requireUser middleware throws a TRPCError with code UNAUTHORIZED if ctx.user is not present. | requireUser |
adminProcedure | Requires an authenticated user with the role of "admin". Throws a TRPCError with code FORBIDDEN if the user is not an admin. | requireUser + admin check |
This diagram shows the middleware flow for a protectedProcedure.
Sources: server/_core/trpc.ts:12-42
Routers
API endpoints are organized into routers. The main appRouter (defined in server/routers.ts, not fully available in context) merges all other feature-specific routers. An example of how a router can be structured is provided in the comments of server/_core/voiceTranscription.ts, demonstrating a voiceRouter with a transcribe mutation. This mutation is a protectedProcedure, meaning only authenticated users can call it.
// Example from server/_core/voiceTranscription.ts:161-188
export const voiceRouter = router({
transcribe: protectedProcedure
.input(z.object({
audioUrl: z.string(),
language: z.string().optional(),
prompt: z.string().optional(),
}))
.mutation(async ({ input, ctx }) => {
const result = await transcribeAudio(input);
if ('error' in result) {
throw new TRPCError({
code: 'BAD_REQUEST',
message: result.error,
cause: result,
});
}
// ... database logic ...
return result;
}),
});
This modular approach allows for clean separation of concerns, with each feature having its own set of endpoints defined in its own router file.
Sources: server/_core/voiceTranscription.ts:161-188, server/_core/trpc.ts:11
Client-Side Implementation
The React Native client uses @trpc/react-query to interact with the backend API. This provides a fully-typed client and React hooks for queries and mutations.
Sources: lib/trpc.ts
Client Creation
A single tRPC client is created and configured in lib/trpc.ts. The configuration specifies the API URL, the superjson transformer, and a mechanism to automatically include the authentication token in the headers of each request.
The client is configured with an httpBatchLink, which bundles multiple tRPC calls into a single HTTP request for efficiency.
| Configuration | Description |
|---|---|
url | The full URL to the tRPC endpoint (/api/trpc). |
transformer | superjson is used to match the server's data transformer. |
headers | An async function that retrieves the session token using Auth.getSessionToken() and adds it as a Authorization: Bearer <token> header. |
fetch | A custom fetch implementation that includes credentials: "include" to support cookie-based authentication on the web. |
Sources: lib/trpc.ts:15-35
Authentication Flow
The client-side tRPC setup seamlessly integrates with the authentication system. Before a request is sent, the headers function in the httpBatchLink is executed.
This sequence diagram details how the client adds the authentication token to a request.
This ensures that every call made through the tRPC client to a protectedProcedure is automatically authenticated without requiring manual token handling in the component code.
Sources: lib/trpc.ts:24-27, lib/_core/api.ts:16-24
Dependencies
The tRPC functionality relies on several key packages defined in package.json.
| Package | Version | Role |
|---|---|---|
@trpc/client | 11.7.2 | Core tRPC client library. |
@trpc/server | 11.7.2 | Core tRPC server library. |
@trpc/react-query | 11.7.2 | Integration with TanStack Query for React hooks. |
superjson | 1.13.3 | Handles serialization/deserialization of complex types. |
zod | 4.2.1 | Used for input validation on server-side procedures. |
Sources: package.json:28-31, 62