Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/Praashh/buildml/llms.txt

Use this file to discover all available pages before exploring further.

Buildml’s entire API layer is built on tRPC v11 — there are no traditional REST endpoints (except for the QStash webhook). Every procedure is fully type-safe end-to-end: your editor knows the exact input and output shape of every call before you ship a single line to production.

Root Router

The root router is assembled in src/server/api/root.ts and combines five sub-routers into a single appRouter export. The exported AppRouter type is the source of truth for all client-side type inference.
// src/server/api/root.ts
import { createCallerFactory, createTRPCRouter } from "~/server/api/trpc";
import { feedbackRouter } from "./routers/feedback";
import { problemRouter } from "./routers/problem";
import { problemSetRouter } from "./routers/problemSet";
import { submissionRouter } from "./routers/submission";
import { userRouter } from "./routers/user";

export const appRouter = createTRPCRouter({
  feedback: feedbackRouter,
  problem: problemRouter,
  problemSet: problemSetRouter,
  submission: submissionRouter,
  user: userRouter,
});

export type AppRouter = typeof appRouter;
export const createCaller = createCallerFactory(appRouter);
The AppRouter type is exported and used by both ~/trpc/react and ~/trpc/server to infer inputs, outputs, and error shapes automatically. You never need to write manual type definitions for API responses — TypeScript propagates them from the router definitions.

Available Routers

problem

Fetch individual problems and their metadata. Used to populate the challenge editor view.

problemSet

Fetch collections of problems grouped into a problem set. Powers the challenge browser and leaderboard pages.

submission

Run code against tests synchronously, or submit for permanent grading via QStash. Includes a getStatus query for polling results.

user

Retrieve leaderboard rankings and individual user profiles. Useful for building progress dashboards.

feedback

Submit user feedback on problems or the platform. No authentication required.

Procedure Types

Buildml defines two procedure types in src/server/api/trpc.ts. Both include a timing middleware that logs execution duration and adds an artificial delay in development to surface loading states.

publicProcedure

No authentication required. Any visitor can invoke a public procedure.
export const publicProcedure = t.procedure.use(timingMiddleware);

protectedProcedure

Requires an active NextAuth session. If the caller is not signed in, tRPC throws a UNAUTHORIZED error before the resolver runs.
export const protectedProcedure = t.procedure
  .use(timingMiddleware)
  .use(({ ctx, next }) => {
    if (!ctx.session?.user) {
      throw new TRPCError({ code: "UNAUTHORIZED" });
    }
    return next({
      ctx: {
        // infers `session` as non-nullable in all protected resolvers
        session: { ...ctx.session, user: ctx.session.user },
      },
    });
  });

HTTP Transport

The tRPC handler is mounted at:
POST /api/trpc/[trpc]
The client uses httpBatchStreamLink with SuperJSON as the transformer, so complex types like Date, Map, and Set round-trip correctly between server and client. Requests from React Client Components carry the header x-trpc-source: nextjs-react; requests from React Server Components carry x-trpc-source: rsc.

Calling from the Server

In React Server Components, import api from ~/trpc/server. This uses a direct server-side caller — no HTTP round-trip occurs.
import { api } from "~/trpc/server";

// Fetch all problem sets (public procedure)
const problemSets = await api.problemSet.getAll();

// Fetch a single problem by slug
const problem = await api.problem.getBySlug({ slug: "implement-sigmoid" });
The HydrateClient helper exported from the same module lets you prefetch queries and dehydrate them into the React tree for client-side cache hydration.
import { api, HydrateClient } from "~/trpc/server";

export default async function Page() {
  void api.problemSet.getAll.prefetch();

  return (
    <HydrateClient>
      <ProblemSetList />
    </HydrateClient>
  );
}

Calling from the Client

In React Client Components, import api from ~/trpc/react. This exposes the full @tanstack/react-query-backed hook API.
import { api } from "~/trpc/react";

export function ProblemSetList() {
  const { data, isLoading } = api.problemSet.getAll.useQuery();

  if (isLoading) return <p>Loading</p>;
  return <ul>{data?.map((ps) => <li key={ps.id}>{ps.title}</li>)}</ul>;
}

Input Validation

Every procedure input is validated with a Zod schema defined inline in the router. When validation fails, tRPC returns a structured error that includes the full ZodError flatten:
{
  "error": {
    "code": "BAD_REQUEST",
    "data": {
      "zodError": {
        "fieldErrors": { "code": ["Required"] },
        "formErrors": []
      }
    }
  }
}

Error Codes

CodeMeaning
BAD_REQUESTInput failed Zod validation or a required argument is missing
UNAUTHORIZEDCaller is not signed in and the procedure is protected
TOO_MANY_REQUESTSPer-user rate limit exceeded (applies to run and submit)
INTERNAL_SERVER_ERRORUnexpected server-side failure
All tRPC errors surface through the standard @trpc/client error-handling path. Use the onError callback in a mutation or the error property on a query to inspect the code and message.

Context

The tRPC context is created in createTRPCContext and is available to every resolver as ctx:
FieldTypeDescription
ctx.prismaPrismaClientDatabase client
ctx.sessionSession | nullNextAuth session; guaranteed non-null in protectedProcedure
ctx.headersHeadersIncoming request headers

Build docs developers (and LLMs) love