Skip to content

rubyists/leopard

Repository files navigation

Leopard NATS ServiceApi Server

Leopard is a small framework for building concurrent NATS Service API workers. It uses Concurrent::FixedThreadPool to manage multiple workers in a single process and provides a minimal DSL for defining endpoints and middleware.

Features

  • Declarative endpoint definitions with #endpoint.

  • Declarative JetStream pull consumers with #jetstream_endpoint.

  • Grouping of endpoints with #group

  • Simple concurrency via #run with a configurable number of instances.

  • JSON aware message wrapper that gracefully handles parse errors.

  • Middleware support using #use.

  • Railway Oriented Design, using Dry::Monads for success and failure handling.

  • Dry::Configurable settings container.

  • #logger defaults to SemanticLogger (adjustable as the #logger= setting)

Requirements

  • Ruby >= 3.4.0

  • A running NATS server with the Service API enabled.

Installation

Add the gem to your project:

# Gemfile
gem 'leopard'

Then install it with Bundler.

$ bundle install

Usage

Create a service class and include Rubyists::Leopard::NatsApiServer. Define one or more endpoints. Each endpoint receives a Rubyists::Leopard::MessageWrapper object for each request to the NATS Service API endpoint that service class is is subscribed to (subject:, or name:). The message handler/callback is expected to return a Dry::Monads[:result] object, typically a Success or Failure.

class EchoService
  include Rubyists::Leopard::NatsApiServer

  endpoint :echo do |msg|
    Success(msg.data)
  end
end

Run the service by providing the NATS connection details and service options:

EchoService.run(
  nats_url: 'nats://localhost:4222',
  service_opts: { name: 'echo' },
  instances: 4
)

Middleware can be inserted around endpoint dispatch:

class LoggerMiddleware
  def initialize(app)
    @app = app
  end

  def call(wrapper)
    puts "received: #{wrapper.data.inspect}"
    @app.call(wrapper)
  end
end

EchoService.use LoggerMiddleware

JetStream Pull Consumers

Leopard can also bind JetStream pull consumers through the same middleware and Dry::Monads::Result handler contract used by request/reply endpoints.

class EventConsumer
  include Rubyists::Leopard::NatsApiServer

  jetstream_endpoint(
    :events,
    stream: 'EVENTS',
    subject: 'events.created',
    durable: 'events-created-worker',
    consumer: { max_deliver: 5 },
    batch: 5,
    fetch_timeout: 1,
    nak_delay: 2,
  ) do |msg|
    Success(msg.data)
  end
end

JetStream handlers receive the same Rubyists::Leopard::MessageWrapper as service endpoints. Leopard will:

  • ack on Success

  • nak on Failure (nak_delay: is optional)

  • term on unhandled exceptions

Each Leopard instances: worker creates its own pull subscription loop, so JetStream consumers scale with the same process-local concurrency model as the rest of the framework.

Development

The project uses Minitest and RuboCop. Run tests with Rake:

$ bundle exec rake ci

This task starts NATS JetStream through ./ci/nats/start.sh, waits for broker health, runs RuboCop and the test suite, and then stops the broker.

API documentation can be generated with:

$ bundle exec rake yard

Documentation coverage is enforced with:

$ bundle exec rake yard:verify

If you want to run the broker yourself, the same script can still be used directly:

$ ./ci/nats/start.sh

Conventional Commits (semantic commit messages)

This project follows the Conventional Commits specification.

To contribute, please follow that commit message format, or your pull request may be rejected.

License

MIT

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Packages

 
 
 

Contributors