{"id":21389847,"url":"https://github.com/nxt-insurance/nxt_http_client","last_synced_at":"2025-10-06T10:31:37.102Z","repository":{"id":37905043,"uuid":"197064979","full_name":"nxt-insurance/nxt_http_client","owner":"nxt-insurance","description":"Super simple http client dsl on top of the typhoeus gem. ","archived":false,"fork":false,"pushed_at":"2025-08-01T00:03:19.000Z","size":248,"stargazers_count":9,"open_issues_count":5,"forks_count":0,"subscribers_count":15,"default_branch":"main","last_synced_at":"2025-09-04T10:34:17.327Z","etag":null,"topics":["dsl","faraday","http-client","httparty","rest-client","ruby","ruby-on-rails","typhoeus"],"latest_commit_sha":null,"homepage":"","language":"Ruby","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/nxt-insurance.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE.txt","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2019-07-15T20:07:59.000Z","updated_at":"2025-02-17T15:21:42.000Z","dependencies_parsed_at":"2024-02-22T14:25:57.683Z","dependency_job_id":"f13ca9c3-1772-49cf-a826-63261c71a832","html_url":"https://github.com/nxt-insurance/nxt_http_client","commit_stats":{"total_commits":187,"total_committers":9,"mean_commits":20.77777777777778,"dds":0.5454545454545454,"last_synced_commit":"4f26dbf9c2a3b5ab6fc4dcd5206f1b9b3a77c4fb"},"previous_names":[],"tags_count":34,"template":false,"template_full_name":null,"purl":"pkg:github/nxt-insurance/nxt_http_client","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nxt-insurance%2Fnxt_http_client","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nxt-insurance%2Fnxt_http_client/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nxt-insurance%2Fnxt_http_client/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nxt-insurance%2Fnxt_http_client/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/nxt-insurance","download_url":"https://codeload.github.com/nxt-insurance/nxt_http_client/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nxt-insurance%2Fnxt_http_client/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":278437297,"owners_count":25986758,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-10-05T02:00:06.059Z","response_time":54,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["dsl","faraday","http-client","httparty","rest-client","ruby","ruby-on-rails","typhoeus"],"created_at":"2024-11-22T13:13:12.824Z","updated_at":"2025-10-06T10:31:36.698Z","avatar_url":"https://github.com/nxt-insurance.png","language":"Ruby","funding_links":[],"categories":[],"sub_categories":[],"readme":"# NxtHttpClient\n\nBuild http clients with ease. NxtHttpClient is a DSL on top of the [typhoeus](https://github.com/typhoeus/typhoeus)\ngem. NxtHttpClient provides configuration functionality to set up HTTP connections on the class level, and attach\ncallbacks that allow you to seamlessly handle responses, as well as configure the original\n`Typhoeus::Request` before making a request.\n\n\n## Installation\n\nAdd this line to your application's Gemfile:\n\n```ruby\ngem 'nxt_http_client'\n```\n\nAnd then execute:\n\n```sh\nbundle\n````\n\n## Usage\n\nWith NxtHttpClient, you can create client classes for interacting with external services:\n\n```ruby\nclass UserServiceClient \u003c NxtHttpClient::Client\n  # Set a base URL, and any other request options you need\n  configure do |config|\n    config.base_url = 'www.example.com'\n    config.request_options.deep_merge!(\n      headers: { API_KEY: '1993' },\n      followlocation: true\n    )\n    config.json_request = true\n    config.raise_response_errors = true\n    config.x_request_id_proc = -\u003e { ('a'..'z').to_a.shuffle.take(10).join }\n  end\n\n  # You may add a log handler if you wish...\n  log do |info|\n    Rails.logger.info(info)\n  end\n\n  # ...as well as a response handler\n  response_handler do |handler|\n    # Note: This error handler is set by default when you use \n    # config.raise_response_errors = true\n    handler.on(:error) do |response|\n      Sentry.set_extras(http_error_details: error.to_h)\n      raise StandardError, \"I can't handle this: #{response.code}\"\n    end\n  end\nend\n```\n\nand then child classes for accessing specific endpoints and adding custom behaviours.\n\n```ruby\nclass UserFetcher \u003c UserServiceClient\n  def initialize(id)\n    @url = \".../users/#{id}\"\n  end\n\n  def fetch_email\n    get(url, { fields: :email }) do |response_handler|\n      response_handler.on(:success) do |response|\n        JSON(response.body)['email']\n      end\n    end\n  end\n\n  def fetch_user_details\n    get(url) do |response_handler|\n      response_handler.on(:success) do |response|\n        body = JSON(response.body)\n        User.new(body)\n      end\n    end\n  end\n\n  private attr_reader :url\nend\n```\n\nUsage:\n\n```ruby\nclient = UserFetcher.new('1234')\nclient.fetch_email\nclient.fetch_user_details\n```\n\nHowever, if you need a simple ad hoc client for a one-off task, you can use `.make` to instantiate one.\n\n```ruby\nclient = NxtHttpClient::Client.make do \n  configure do |config|\n    config.base_url = 'www.httpstat.us'\n    config.request_options.deep_merge!(\n      headers: { API_KEY: '1993' },\n      followlocation: true\n    )\n    config.json_request = true\n    config.json_response = true\n  end\nend\n\nclient.get('/data')\nclient.post('/data', body: { some: 'content'})\n```\n\n### configure\n\nRegister your default request options on the class level. Available options are:\n- `request_options`, passed directly to the underlying Typhoeus Request\n- `base_url=`\n- `x_request_id_proc=`\n- `json_request=`: Shorthand to set the Content-Type request header to JSON and automatically convert request bodies to JSON\n- `json_response=`: Shorthand to set the Accept request header and automatically convert success response bodies to JSON\n- `raise_response_errors=`: Makes the client raise a `NxtHttpClient::Error` for a non-success response. \n  You can also do this manually by setting a response_handler.\n- `bearer_auth=`: Set a bearer token to be sent in the Authorization header\n- `basic_auth=`: Pass a Hash containing `:username` and `:password`, to be sent as Basic credentials in the Authorization header\n- `timeouts(total:, connect: nil)`: Configure timeouts\n\n### response_handler\n\nRegister a default response handler for your client class. You can reconfigure or overwrite this in subclasses and\non the instance level.\n\n### fire\n\nAll http methods internally are delegate to `fire(uri, **request_options)`. Since `fire` is a public method you can\nalso use it to fire your requests and use the response handler to register callbacks for specific responses.\n\nRegistered callbacks have a hierarchy by which they are executed. Specific callbacks will come first\nand more common callbacks will come later in case none of the specific callbacks matched. It this is not what you want you\ncan simply put the logic you need into one common callback that is called in any case. You can also use strings with wildcards\nto match a group of response by status code. `handler.on('4**') { ... }` basically would match all client errors.   \n\n```ruby\nfire('uri', **request_options) do |handler|\n  handler.on(:any) do |response|\n    raise StandardError, 'This would overwrite all others since it matches first'\n  end\n\n  handler.on(:success) do |response|\n    response.body\n  end\n\n  handler.on(:timed_out) do |response|\n    raise StandardError, 'Timeout'\n  end\n\n  handler.on(:error) do |response|\n    raise StandardError, 'This is bad'\n  end\n\n  handler.on(:others) do |response|\n    raise StandardError, 'Other problem'\n  end\n\n  handler.on(:headers) do |response|\n    # This is already executed when the headers are received\n  end\n\n  handler.on(:body) do |chunk|\n   # Use this to stream the body in chunks\n  end\nend\n```\n\n### Callbacks around fire\n\nNext to implementing callbacks for handling responses there are also callbacks around making requests. Note tht you can\nhave as many callbacks as you want. In case you need to reset them because you do not want to inherit them from your\nparent class (might be a smell when you need to...) you can reset callbacks via `clear_fire_callbacks` on the class level.\n\n```ruby\n\nclear_fire_callbacks # Call this to clear callbacks setup in the parent class\n\nbefore_fire do |client, request, response_handler|\n  # here you have access to the client, request and response_handler  \nend\n\naround_fire do |client, request, response_handler, fire|\n  # here you have access to the client, request and response_handler\n  fire.call # You have to call fire here and return the result to the next callback in the chain\nend\n\nafter_fire do |client, request, response, result, error|\n  result # The result of the last callback in the chain is the result of fire!\nend\n```\n\n\n### NxtHttpClient::Error\n\nNxtHttpClient also provides an error base class that you might want to use as the base for your client errors.\nIt comes with a nice set of useful methods. You can ask the error for the request and response options since it\nrequires the response for initialization. Furthermore it has a handy `to_h` method that provides you all info about\nthe request and response.\n\n#### Timeouts\n**You must set a timeout on every request (or when configuring the client class).** \nOtherwise, this gem will raise an error. The idea is to enforce the best practice \nof [always setting a timeout](https://dev.to/bearer/the-importance-of-request-timeouts-l3n).\n\nTo set a timeout, use the `timeout_seconds` config method:\n\n```rb\nconfigure do |config|\n  config.timeout_seconds(total: 10)\n  # You can also set a connect timeout\n  config.timeout_seconds(total: 10, connecttimeout: 2)\nend\n```\n\nNxtHttpClient::Error exposes the `timed_out?` method from `Typhoeus::Response`, so you can check if an error is raised due to a timeout. \nThis is useful when setting a custom timeout value in your configuration.\n\n### Logging\n\nNxtHttpClient also comes with a log method on the class level that you can pass a proc if you want to log your request.\nYour proc needs to accept an argument in order to get access to information about the request and response made.  \n\n```ruby\nlog do |info|\n  Rails.logger.info(info)\nend\n\n# info is a hash that is implemented as follows:\n\n{\n  client: client,\n  started_at: started_at,\n  request: request,\n  finished_at: now,\n  elapsed_time_in_milliseconds: finished_at - started_at,\n  response: request.response,\n  http_status: request.response\u0026.code\n}\n```\n\n### Caching\n\nTyphoeus ships with caching built in. Checkout the [typhoeus](https://github.com/typhoeus/typhoeus) docu to figure out\nhow to set it up. NxtHttpClient builds some functionality on top of this and offer to cache requests within the current\nthread or globally. You can simply make use of it by providing one of the caching options `:thread` or`:global` as config\nrequest option or the actual request options when building the request.\n\n```ruby\nclass Client \u003c NxtHttpClient::Client\n  configure do |config|\n    config.request_options = { cache: :thread }\n  end\n\n  response_handler do |handler|\n    handler.on(200) do |response|\n      # ...\n    end\n  end\n\n  def call\n    get('.../url.com', cache: :thread) # configure caching per request level\n  end\nend\n```\n\n## Development\n\nAfter checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.\n\nTo install this gem onto your local machine, run `bundle exec rake install`.\n\n## Releasing\n\n### RubyGems \n\nFirst, if you don't want to always log in with your RubyGems password, \nyou can create an API key on Rubygems.org, and then run:\n\n```shell\nbundle config set --local gem.push_key rubygems\n```\n\nAdd to `~/.gem/credentials` (create if it doesn't exist):\n\n```shell\n:rubygems: \u003cyour Rubygems API key\u003e\n```\n\nTo release a new version follow the steps strictly: \n\n- Commit all your feature changes\n- Update the version number in `version.rb`,\n- Run bundle install to update the Gemfile.lock\n- Open your PR and get it approved and merged\n- And then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).\n\n### Github Package Registry \n\nTo release a new version follow the steps strictly: \n\n- Commit all your feature changes\n- Update the version number in `version.rb`,\n- Run bundle install to update the Gemfile.lock\n- Open your PR and get it approved and merged\n- Checkout to main and then run `bin/release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to the github package registry.\n\nBefore releasing a new version, make sure you have authenticated with the Github package registry. To do so, create a personal access token ([in your Github account settings](https://github.com/settings/tokens))\n\nThen create or add to the existing file ~/.gem/credentials, replacing `TOKEN` with your personal access token.\n\n```\n---\n:github: Bearer TOKEN\n```\n\nThen run \n\n```sh\nbundle config set --local gem.push_key github\n```\n\n## Contributing\n\nBug reports and pull requests are welcome on GitHub at https://github.com/nxt-insurance/nxt_http_client.\n\n## License\n\nThe gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnxt-insurance%2Fnxt_http_client","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fnxt-insurance%2Fnxt_http_client","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnxt-insurance%2Fnxt_http_client/lists"}