Skip to content

SwarnimWalavalkar/hermes

Repository files navigation

Hermes

Type safe message bus, and "request-response" style services. Powered by Redis Streams

Installation

pnpm add @swarnim/hermes
npm i @swarnim/hermes

Features

  • Type-safety
  • Schema validation with Zod
  • Horizontally scalable
  • Reliability
  • Redis Streams do the heavy lifting
  • consumers need to explicitly acknowledge that a message has been processed
  • If (when) a consumer dies, all of the pending messages assigned to that consumer are transferred to another consumer after a timeout
  • Retries with exponential backoff
    • Upto a configurable maxRetries number of times
    • Automatically triggered on failures

Example

Type-Safe Service Type Safe Service Example

Type-Safe Message Bus Type-Safe Message Bus

Usage

// Instantiate and connect to Hermes
const hermesTest = await Hermes({
  poolOptions: { min: 0, max: 20 },
  durableName: "playground",
  redisOptions: {
    host: process.env.REDIS_HOST || "0.0.0.0",
    port: Number(process.env.REDIS_PORT) || 6379,
    password: process.env.REDIS_PASSWORD || "",
  },
}).connect();

/** SERVICES **/

// Register a service
const sayHelloService = await hermesTest.registerService(
  "say-hello",
  z.object({
    name: z.string(),
    age: z.number(),
    favorites: z.object({ color: z.string() }),
  }),
  z.object({ message: z.string() })
);

// Register a reply handler for that service
sayHelloService.reply(({ reqData, msgId }) => {
  return { message: `Hello, ${reqData.name}!` };
});

// Make a request to that service, the reply handler should process it, and return a response
const response = await sayHelloService.request({
  name: "Swarnim",
  age: 12,
  favorites: { color: "Azure" },
});

// Print out the response
console.log("GOT_RESP", response);

/** MESSAGE BUS **/

// Register an event
const userSignUpEvent = await hermesTest.registerEvent(
  "user-signup",
  z.object({
    userId: z.number(),
    username: z.string(),
    deviceType: z.enum(["desktop", "mobile"]),
  })
);

// Register a subscriber handler to that event
userSignUpEvent.subscribe(async ({ data, msg }) => {
  console.log("RECEIVED USER SIGNUP EVENT", data);
  await msg.ack();
});

// Publish event, the subscriber handler should be invoked
await userSignUpEvent.publish({
  userId: 1,
  username: "testUser",
  deviceType: "desktop",
});

// ...

// During application teardown
await hermesTest.disconnect();

Feature Ideas

  • Job scheduler
  • Better concurrency support
  • Allow bulk publishing messages
    • With pipelining to optimize Redis calls and effective chunking
  • Message de-duplication

Contributing

This project follows the all-contributors specification. Contributions of any kind are welcome!

LICENSE

MIT

About

Type safe message bus, and RPC style services. Powered by Redis Streams

Topics

Resources

License

Stars

Watchers

Forks

Packages

No packages published