Skip to content

First Route

This guide adds a GET /users/:userId endpoint to an existing Routa project. By the end you will have a validated path parameter, a typed response, and a route that appears in generated metadata and OpenAPI.

  • A Routa project created with create-routa-ts, or an existing project set up through Installation.
  • The development server stopped. You will restart it in the last step.
  1. Create the schema module. Keeping schemas beside the route is the default convention, and exported names become OpenAPI component names.

    src/routes/users/$userId/schemas.ts
    import { z } from "zod";
    export const GetUserParams = z.object({
    userId: z.string().min(1),
    });
    export const GetUserResponse = z.object({
    id: z.string(),
    name: z.string(),
    });
    export const UserNotFoundResponse = z.object({
    message: z.string(),
    });
  2. Create the route file. The folder path becomes the URL path, and $userId becomes the :userId parameter.

    src/routes/users/$userId/route.ts
    import { createRoute, createRouteRoot } from "@routa-ts/core";
    import { GetUserParams, GetUserResponse, UserNotFoundResponse } from "./schemas.js";
    const route = createRouteRoot("/users/:userId");
    export default route({
    get: createRoute({
    input: {
    params: GetUserParams,
    },
    responses: {
    success: {
    status: 200,
    schema: GetUserResponse,
    },
    notFound: {
    status: 404,
    schema: UserNotFoundResponse,
    },
    },
    run: async ({ input }) => {
    if (input.params.userId !== "usr_1") {
    return { type: "notFound", data: { message: "User not found." } };
    }
    return { type: "success", data: { id: input.params.userId, name: "Ada" } };
    },
    }),
    });
  3. Generate route metadata so the new path is registered and typed.

    Terminal window
    npm run generate
    Generated .routa/routes.gen.ts for 2 route file(s).
  4. Start the development server.

    Terminal window
    npm run dev
    INFO api.started Routa API started. {"host":"127.0.0.1","port":3000,"routes":2}

Both declared outcomes are reachable, and each returns its declared status:

Terminal window
curl -i http://127.0.0.1:3000/users/usr_1
HTTP/1.1 200 OK
content-type: application/json; charset=utf-8
{"id":"usr_1","name":"Ada"}
Terminal window
curl -i http://127.0.0.1:3000/users/usr_2
HTTP/1.1 404 Not Found
content-type: application/json; charset=utf-8
{"message":"User not found."}

createRouteRoot("/users/:userId") binds the file to a path and gives the methods inside it the context type generated for that path. createRoute declares one method. Routa parses input.params through GetUserParams before run executes, so input.params.userId is a validated string rather than string | undefined.

The handler returns { type, data } where type is a key of responses. TypeScript rejects any other name, and at runtime Routa validates data against that outcome’s schema before serializing it with the declared status.

Problem Fix
ROUTA_ROUTE_CONFIG_UNRESOLVED The file must have a default export produced by the createRouteRoot(path) helper. Assigning the config to a variable that is exported later cannot be resolved statically.
ROUTA_MISSING_SUCCESS_RESPONSE Every method needs at least one response with a 2xx status.
ROUTA_RESULT_TYPE The handler returned a name that is not a key of responses. Check for a typo or add the outcome.
The route returns 404 for every request. Metadata is stale. Run routa generate and restart the server.