Suvidha is designed for type safety and an improved developer experience with Express.js.

Let’s take a look at the typical flow of request and response handling.

Authentication

Validates user identity by checking the provided JWT token, API key, or session cookie before proceeding.

Authorization

Verifies if the authenticated user has the required permissions to access the requested resource.

Validation

Ensures all request data is properly formatted and meets the required validation rules.

Resource

Executes the main business logic to handle the request and prepare the appropriate response.

Below is an example implementation of it in express.js.

I have tried to keep it simple.

app.post(
    "/books",
    authenticate,
    authorize,
    (req, res) => validateBody(req, res, BookSchema),
    async function (req, res) {
        // Instead of `try-catch` usually we use "asyncHandler()"
        try {
            await handler(req, res);
        } catch (err) {
            res.status(500).json({
                error: "Internal Server Error",
            });
        }
    },
);

Popular solutions:

  • asyncHandler() to avoid try-catch boilerplate.
  • extend Express’ Request interface globally to add user.

It’s patchwork to add user property to Request interface globally. Types are not inferred by typescript, middlewares and request handlers act independently, and on a leap of faith that everyone is doing their job right.

Suvidha is a wrapper around your request handler, that addresses all these type-safety issues.

Re-writing the above example using Suvidha:

// Order authenticate -> authorize is enforced at compile-time
app.post(
    "/books",
    suvidha()
        .use(authenticate)
        .use(authorize)
        .body(BookSchema)
        .handler(async (req, _res) => {
            const { user } = req.context; // type of user: { role: string, name: string }
            const book = req.body; // type of book: { name: string, author: string }
            return handler(book, user);
        }),
);

If you noticed, we never used _res: Response anywhere to send response, because that’s handled by Handlers for us. If you want to send a response manually, you can still do res.send() and it will work fine.

To understand how Suvidha facilitates this, let’s look at the flow.

Flow

Lines are highlighted wherever the Handlers and use middlewares are called including request handler.

Start Request]
    |
    v
[Initialize Context]
    |
    v
[Validation/Middleware Loop (Ordered)]
    |
    +-- For each item in order (Validation or Middleware):
    |    |
    |    v
    |    [Is it Validation?]
    |        |
    |        +-- Yes: [Parse Validation (body/query/params)]
    |        |        |
    |        |        +-- Error?
    |        |            |
    |        |            v
    |        |            [ZodError] --> onSchemaErr(error, conn)
    |        |                        |
    |        |                        +-- Headers Sent? --> Return
    |        |                        |
    |        |                        +-- Not Sent --> Log & Re-throw Err
    |        |
    |        +-- No: [Execute Middleware]
    |                 |
    |                 v
    |                 [Middleware Function] --> Update Context
    |                 |
    |                 +-- Headers Sent? --> Return
    |
    |
    v
[Execute Handler (User-provided)] --> Output
    |
    v
[Check Headers Sent]
    |
    +-- Yes --> onPostResponse(output, conn, next)
    |
    +-- No --> onComplete(output, conn, next)
    |
    v
[Error Occurs (Anywhere above)]
    |
    v
[Catch Block]
    |
    v
[Check Headers Sent]
    |
    +-- Yes --> onPostResponse(error, conn, next)
    |
    +-- No --> onErr(error, conn, next)

That’s pretty much what Suvidha is. It just facilitates the flow by adding a layer of abstraction and type-safety. Handlers have the control over how you process the request and response.