Skip to content

refinist/hono-json-response

Repository files navigation

Hono jsonResponse Middleware npm codecov

A Hono middleware for standardized JSON responses

Why Choose This Middleware?

❌ Traditional Approach: Repetitive and Error-Prone

return c.json({ code: 2000, data: users, msg: 'Operation successful' });
return c.json({ code: 2000, data: roles, msg: 'Operation successful' });
return c.json({ code: 5000, data: null, msg: 'Username already exists' });
return c.json({ code: 5100, data: null, msg: 'System error' });

βœ… Using Middleware: Semantic and Concise

return c.ok(users, 'Operation successful');
return c.ok(roles); // No need to pass "Operation successful" every time, as it's the default
return c.bizerr(null, 'Username already exists');
return c.syserr();

Features

  • πŸš€ Unified JSON Response Format - Standardized API response structure
  • 🎯 Semantic Response Methods - c.ok() is more intuitive than c.json({code: 2000})
  • πŸ”§ Eliminate Boilerplate Code - Say goodbye to repetitive c.json({code: xxx, data: xxx, msg: xxx}) patterns
  • πŸ“ Predefined Common Scenarios - ok(success), unauth(unauthorized), bizerr(business error), syserr(system error)
  • πŸ› οΈ Flexible Extension - Support for custom response methods and status codes
  • πŸ’ͺ Type Safety - Complete TypeScript support and intelligent hints

Install

# npm
npm install hono-json-response

# yarn
yarn add hono-json-response

# pnpm
pnpm add hono-json-response

# bun
bun add hono-json-response

Basic Usage

import { Hono } from 'hono';
import { jsonResponse } from 'hono-json-response';

const app = new Hono();
app.use('*', jsonResponse());
app.get('/getUserList', c => {
  return c.ok(
    {
      list: [{ name: 'John' }, { name: 'Jane' }],
      totals: 100
    },
    'Get user list successfully'
  );
});

Response Format

Default Format

All responses follow a unified JSON format (code, data, msg):

{
  "code": 2000,
  "data": null,
  "msg": ""
}

Custom Field Names

Support custom response field names to adapt to different project API specifications:

app.use(
  '*',
  jsonResponse(null, {
    code: 'status', // code -> status
    data: 'result', // data -> result
    msg: 'message' // msg -> message
  })
);

Predefined Methods

c.ok(data?, msg?)

Success response

  • Status Code: 2000
  • Usage: When operation is successful
app.get('/getUserList', c => {
  return c.ok(
    {
      list: [{ name: 'John' }, { name: 'Jane' }],
      totals: 100
    },
    'Get user list successfully'
  );
});

c.unauth(data?, msg?)

Unauthorized response

  • Status Code: 4000
  • Usage: When user is not logged in or token is invalid
app.post('/login', c => {
  return c.unauth();
});

c.bizerr(data?, msg?)

Business error response

  • Status Code: 5000
  • Usage: Business logic errors (such as duplicate accounts, insufficient inventory, etc.)
app.post('/register', async c => {
  const { name } = await c.req.json();
  return c.bizerr({ name }, `Username ${name} already exists`);
});

c.syserr(data?, msg?)

System error response

  • Status Code: 5100
  • Usage: System-level errors (such as database connection failure, processing exceptions, etc.)
app.post('/foo', c => {
  try {
    // do something
  } catch (error) {
    return c.syserr(error);
  }
});

c.jr(code, data?, msg?)

Custom response code

  • Parameter: code - Custom status code
  • Usage: Some complex interfaces may need to return more status codes to represent different business logic
app.get('/orders/:id', async c => {
  const orderId = c.req.param('id');

  try {
    const order = await getOrderById(orderId);

    // Order not found
    if (!order) {
      return c.jr(5000, null, 'Order not found');
    }

    // No permission to access other user's order
    if (order.userId !== userId) {
      return c.jr(5001, null, 'No permission to access this order');
    }

    // Order has been deleted
    if (order.status === 'deleted') {
      return c.jr(5002, null, 'Order has been deleted');
    }
    // More business logic...

    return c.ok(order, 'Get order details successfully');
  } catch (error) {
    return c.syserr(null, 'Failed to get order details');
  }
});

Custom Configuration

Override Default Configuration

app.use(
  '*',
  jsonResponse({
    ok: { code: 20000, defaultMsg: 'Override ok' },
    unauth: { code: 40000, defaultMsg: 'Override unauth' }
  })
);

Add Custom Methods

import type { JSONResponseHandler } from 'hono-json-response';

// Don't forget to use TypeScript module augmentation to extend Context type
declare module 'hono' {
  interface Context {
    warning: JSONResponseHandler;
    forbidden: JSONResponseHandler;
  }
}

app.use(
  '*',
  jsonResponse({
    warning: { code: 2001, defaultMsg: 'Warning message' },
    forbidden: { code: 4001, defaultMsg: 'Access forbidden' }
  })
);

app.get('/warning', c => {
  return c.warning(data, 'API will be deprecated');
});

Handle Status Code Conflicts

When custom method status codes conflict with default methods, default methods will be automatically removed:

app.use(
  '*',
  jsonResponse({
    mySuccess: { code: 2000 } // Conflicts with default ok method
  })
);

// At this point, c.ok method is no longer available, only c.mySuccess is available

License

MIT

Copyright (c) 2025-present, Zhifeng (Jeff) Wang

About

πŸ”₯ A Hono middleware for standardized JSON responses

Topics

Resources

License

Stars

Watchers

Forks

Packages

No packages published