> ## Documentation Index
> Fetch the complete documentation index at: https://suvidhajs.is-cool.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# API

> Guide to Suvidha's APIs

<iframe width="560" height="315" src="https://www.youtube.com/embed/ZgmxOb7BEmo?si=gYMEddxMAUJpCiea" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen />

# Core API Methods

Suvidha uses `CtxRequest` which extends the standard Express.js `Request` object with a `context` property,
for accessing request-specific data added by middlewares.

```ts theme={"system"}
/**
 * Extends the standard Express.js `Request` object with a `context` property.
 * This allows you to store and access request-specific data throughout the
 * request lifecycle, particularly useful for middleware communication and
 * data sharing between middleware and request handler.
 *
 * @template C The type of the context object. Defaults to an empty object `{}`.
 *
 * @extends Express.js Request object
 */
interface CtxRequest<C extends Context = {}, ...> extends Request<...> {
    context: C;
}
```

<AccordionGroup>
  <Accordion title="Suvidha(handlers: Handlers)" icon="code">
    Main class for creating a new instance of Suvidha; requires a Handlers instance.

    ```ts theme={"system"}
    constructor(handlers: Handlers);
    ```
  </Accordion>

  <Accordion title="Handlers" icon="code">
    Refer to [Handlers](/reference/handlers) for more details.
  </Accordion>

  <Accordion title=".body(schema: ZodType)" icon="code">
    ### Purpose

    Validates and types the request body using Zod schemas.

    ### Parameters

    * `schema`: Zod schema definition.
    * Returns: Chainable Suvidha instance.

    ### Example

    ```ts theme={"system"}
    suvidha().body(
        z.object({
            email: z.string().email(),
            password: z.string().min(8),
        }),
    );
    ```

    ### Behavior

    * Validates request body before handler execution.
    * Overrides default `any` type with schema inference.
  </Accordion>

  <Accordion title=".params(schema: ZodType)" icon="code">
    ### Purpose

    Validates and types URL path parameters.

    ### Example

    ```ts theme={"system"}
    suvidha().params(
        z.object({
            userId: z.string().uuid(),
            postId: z.coerce.number(),
        }),
    );
    ```

    ### Access in Handler

    ```ts theme={"system"}
    handler((req) => {
        console.log(req.params.userId); // Type-safe string
    });
    ```
  </Accordion>

  <Accordion title=".query(schema: ZodType)" icon="code">
    ### Purpose

    Validates and types query string parameters.

    ### Example

    ```ts theme={"system"}
    suvidha().query(
        z.object({
            page: z.number().default(1),
            sort: z.enum(["asc", "desc"]),
        }),
    );
    ```

    ### Default Values

    ```ts theme={"system"}
    // If ?page=3 not provided
    req.query.page; // 1 (from default)
    ```
  </Accordion>

  <Accordion title=".use(middleware)" icon="code">
    ### Purpose

    Builds request context through chainable middleware. Each call merges new properties into the context while preserving type safety.
    Any exceptions thrown will be handled by `onErr` handler.

    <AccordionGroup>
      <Accordion title="Key Characteristics" icon="key">
        * **Order-Sensitive**: Middleware executes in the order of declaration.
        * **Type Accumulation**: Each middleware's return type merges with previous context.
        * **Immutable Context**: Each middleware gets current context in `Readonly` wrapper to prevent mutations.
        * **Early Termination**: Middleware can end response with `res.send()`.
      </Accordion>
    </AccordionGroup>

    <Note>Merging is the same as using the `...` spread operator, `new_ctx = { ...current_ctx, ...ctx }` </Note>

    ### Middleware Signature

    ```ts theme={"system"}
    type Middleware<Ctx> = (
        req: CtxRequest<CurrentContext>,
        res: Response,
    ) => Ctx | Promise<Ctx>;
    ```

    ### Example

    ```ts theme={"system"}
    .use(async (req) => ({
      user: await authenticate(req)
    }))
    .use((req) => ({
      permissions: getPermissions(req.context.user)
    }))
    ```

    ### Type Safety

    ```ts theme={"system"}
    // In handler:
    req.context.user; // AuthenticatedUser
    req.context.permissions; // string[]
    ```
  </Accordion>

  <Accordion title=".handler(fn)" icon="code">
    ### Purpose

    Defines the final request handler with full type safety.

    ### Signature

    ```ts theme={"system"}
    .handler(
      (req: CtxRequest, res: Response, next: NextFunction) => Reply
    )
    ```

    ### Parameters

    * `req` is express' `Request` object with added context.
    * `res` is express' `Request` object.
    * `next` is express' `NextFunction` to call next middleware.
  </Accordion>

  <Accordion title=".next()" icon="code">
    Acts as an Express.js middleware. It executes the configured **Suvidha middleware** functions (both validation via `.body()`, `.query()`, `.params()` and custom logic via `.use()`) in the order they are defined. After successful execution of these middlewares, `.next()` calls the next middleware in the Express.js chain using the express' `next()` function.

    It's like `handler()` but with request handler as express' `next()` function.

    ### Example

    ```ts theme={"system"}
    const bannedHosts = new Set(["malicious.example.com", "spam.org"]);

    app.use(
        "/protected",
        suvidha()
            .use(async (req) => {
                const clientHost = req.hostname;
                if (bannedHosts.has(clientHost)) {
                    throw Http.Forbidden.body({
                        message: `Host "${clientHost}" is banned.`,
                    });
                }
                return {};
            })
            .query(z.object({ resourceId: z.string() }))
            .next(), // Proceed to the protected resource handler
        (req, res) => {
            // Access validated query parameter
            const resourceId = req.query.resourceId; // infered type: string
            res.json({ message: `Access granted to resource: ${resourceId}` });
        },
    );
    ```
  </Accordion>
</AccordionGroup>

<Note>
  The execution order of middlewares is the same as the order of declaration.

  ```ts theme={"system"}
  app.get(
      "/reports",
      suvidha()
          .query(pageSchema)
          .use(middyA)
          .use(middyB)
          .params(IdSchema)
          .handler((req) => {
              // Business logic
          }),
  );
  ```

  Execution order is `query` -> `middyA` -> `middyB` -> `params` -> `handler`.
</Note>

# Usage

## Data Validation

Validate the request body using Zod schemas.

```ts {10, 12} theme={"system"}
const userSchema = z.object({
    username: z.string().min(3),
    email: z.string().email(),
    age: z.number().min(13),
});

app.post(
    "/users",
    suvidha()
        .body(userSchema)
        .handler(async (req) => {
            const newUser = req.body; // type of newUser: z.infer<typeof UserSchema>
            // Execute business logic
        }),
);
```

## Writing Middlewares

middlewares take `CtxRequest` and `Response` as input arguments and return `Context | Promise<Context>`.

```ts theme={"system"}
// Middleware expects { user: User } in the context.
// specify the constraint in type signature, that way
// Suvidha will ensure that middleware is called
// only when expected context data is available.
type ExpectedContext = { user: User };

const middleware = (req: CtxRequest<ExpectedContext>, res: Response) => {
    // business logic
    return {
        // return context that will be merged with the current context
    };
};
```

<Tabs>
  <Tab title="works">
    ```ts theme={"system"}
    app.get(
        "/reports",
        suvidha()
            .use(() => ({ user: authenticate() })) // { auth: Auth }
            .use(middleware) // { user: User }
            .handler((req) => {
                // Business logic
            }),
    );
    ```
  </Tab>

  <Tab title="doesn't work">
    ```ts {4} theme={"system"}
    app.get(
        "/reports",
        suvidha()
            .use(middleware) // error: middleware expects { user: User } in context
            .use(() => ({ user: authenticate() })) // { auth: Auth }
            .handler((req) => {
                // Business logic
            }),
    );
    ```

    TypeScript will throw an error because `middleware` expects `{ user: User }` in context,
    but the context is `{}` because the `authenticate` middleware has not been called yet.

    ```text typescript error [2345] theme={"system"}
    Property 'user' is missing in type '{}' but required in type 'ExpectedContext'.
    ```
  </Tab>
</Tabs>

By specifying the constraint in type signature, we can build type-safe context through ordered middlewares - TypeScript enforces dependency sequence:

```ts theme={"system"}
app.get(
    "/reports",
    suvidha()
        .use(() => ({ auth: getAuth() })) // { auth: Auth }
        .use((req) => ({
            // Requires previous auth
            user: getUser(req.context.auth), // + { user: User }
        }))
        .use(async (req) => ({
            // Requires user
            report: await fetchReport(req.context.user), // + { report: Report }
        }))
        .handler((req) => req.context.report), // Final: auth + user + report
);
```

Reorder middlewares → Type error ← Context dependencies broken.

## Loosely Coupled Business Logic

Handles responses using `onComplete` and `onErr` when using `Suvidha` with `Handlers`.
This separates your core business logic from framework-specific response methods, keeping your code cleaner by delegating response handling to the handlers.

```ts theme={"system"}
const UserSchema = z.object({
    username: z.string().min(3),
    email: z.string().email(),
});

type UserDTO = z.infer<typeof UserSchema>;

// Keep your business logic clean and loosely coupled
const requestHandler = (user: UserDTO, role: string) => {
    // Execute business logic
    return {
        id: "67619c28758da37270b925a8",
    };
};

app.post(
    "/users",
    suvidha()
        .use(authenticate)
        .use(roleCheck)
        .body(UserSchema)
        .handler(async (req) => {
            const newUser = req.body;
            const { role } = req.context.user;
            return requestHandler(newUser, role); // Execute business logic
        }),
);
```

## Suvidha as data validation middleware

To use Suvidha solely as data validation middleware with TypeScript inference, implement `onSchemaErr` method.

```ts {38} theme={"system"}
// Customize the error response
class CustomHandlers implements Handlers {
    onSchemaErr(_: ZodError, conn: Conn) {
        const fmt = {
            success: false,
            error: "VALIDATION_FAILURE",
        };
        conn.res.status(400).json(fmt);
    }

    onErr(): Promise<void> | void {
        throw new Error("Method not implemented.");
    }

    onComplete(): Promise<void> | void {
        throw new Error("Method not implemented.");
    }

    onPostResponse(): Promise<void> | void {
        throw new Error("Method not implemented.");
    }
}

const userSchema = z.object({
    username: z.string().min(3),
    email: z.string().email(),
});

const verifyBody = <T extends z.ZodTypeAny>(schema: T) =>
    suvidha().body(schema).next();

app.post("/users", verifyBody(userSchema), (req, res) => {
    // type of req.body: z.infer<typeof userSchema>
    const { email, username } = req.body;
    res.send({ email, username });
});
```

## DRY Suvidha

If you have common middlewares across multiple routes, you can create a `Suvidha` instance and reuse it.

```ts {8, 17, 29, 39} theme={"system"}
import { Suvidha, DefaultHandlers, Http } from "suvidha";

declare function authenticate(req: Request): Promise<{ role: string }>;

const suvidha = () => Suvidha.create(new DefaultHandlers());

// instance with middlewares for authentication
const auth = () =>
    suvidha().use(async (req) => {
        const user = await authenticate(req).catch((_) => {
            throw new Http.Unauthorized();
        });
        return { user };
    });

// instance with middlewares for admin authorization
const adminAuth = () =>
    // Reuse auth middleware
    auth().use((req) => {
        if (req.context.user.role !== "admin") {
            throw new Http.Forbidden();
        }
        return {};
    });

// Admin protected user creation endpoint
app.post(
    "/users",
    adminAuth()
        .body(userSchema)
        .handler(async (req) => {
            // Business logic
        }),
);

// Anyone authenticated user can access reports
app.get(
    "/reports",
    auth()
        .query(pageSchema)
        .handler(async (req) => {
            // Business logic
        }),
);
```

## Adopting Suvidha in Existing Projects

Suvidha's `CtxRequest` extends the standard `Request` object by adding a `context` property.
Existing handlers that accept `Request` will also work with `CtxRequest`. The `context` can be accessed using `CtxRequest`.

<CodeGroup>
  ```ts Before theme={"system"}
  declare function identity(req: Request): Promise<User>;

  const BookSchema = z.object({
      title: z.string().min(3),
      author: z.string().min(3),
  });

  type Book = z.infer<typeof BookSchema>;
  type User = { id: string; name: string };

  function validateBody(req: Request, res: Response, zodSchema: ZodType<any>) {
      try {
          req.body = zodSchema.parse(req.body);
          next();
      } catch (err) {
          return void res.status(400).send("Bad Request");
      }
  }

  const authenticate = async (
      req: Request,
      res: Response,
      next: NextFunction,
  ) => {
      try {
          (req as any).user = await identity(req);
          next();
      } catch (_) {
          return void res.status(401).json({ error: "Unauthorized" });
      }
  };

  async function createBookHandler(
      req: Request<any, any, Book>,
      res: Response,
  ): Promise<void> {
      const user = (req as any).user as User;
      const book = req.body;
      // Business logic ...
  }

  app.post(
      "/books",
      authenticate,
      (req, res) => validateBody(req, res, BookSchema),
      createBookHandler,
  );
  ```

  ```ts After {14, 22, 25} theme={"system"}
  // accepts `CtxRequest` as well
  declare function identity(req: Request): Promise<User>;

  const BookSchema = z.object({
      title: z.string().min(3),
      author: z.string().min(3),
  });

  type Book = z.infer<typeof BookSchema>;
  type User = { id: string; name: string };

  const authenticate = async (req: CtxRequest) => {
      // type of req: CtxRequest
      const user = await identity(req).catch((_) => {
          throw new Http.Unauthorized();
      });
      return { user }; // merge `{ user }` to context
  };

  // Use `CtxRequest` in handler, to get context data
  function createBookHandler(
      req: CtxRequest<{ user: User }, any, any, Book>,
      res: Response,
  ): Promise<void> {
      const user = req.context.user;
      const book = req.body;
      // Business logic ...
  }

  app.post(
      "/books",
      suvidha()
          .use(authenticate)
          .body(BookSchema)
          .handler((req, res) => createBookHandler(req, res)),
  );
  ```
</CodeGroup>
