{"id":13879179,"url":"https://github.com/rubygarage/api_struct","last_synced_at":"2025-07-16T15:31:40.772Z","repository":{"id":48768952,"uuid":"128056105","full_name":"rubygarage/api_struct","owner":"rubygarage","description":"API wrapper builder with response serialization","archived":false,"fork":false,"pushed_at":"2022-10-06T00:12:38.000Z","size":101,"stargazers_count":233,"open_issues_count":11,"forks_count":20,"subscribers_count":5,"default_branch":"master","last_synced_at":"2025-06-23T23:45:58.352Z","etag":null,"topics":["api","ruby","serialization","wrapper"],"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/rubygarage.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}},"created_at":"2018-04-04T12:02:44.000Z","updated_at":"2025-05-19T22:46:05.000Z","dependencies_parsed_at":"2022-08-26T22:10:59.207Z","dependency_job_id":null,"html_url":"https://github.com/rubygarage/api_struct","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/rubygarage/api_struct","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rubygarage%2Fapi_struct","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rubygarage%2Fapi_struct/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rubygarage%2Fapi_struct/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rubygarage%2Fapi_struct/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/rubygarage","download_url":"https://codeload.github.com/rubygarage/api_struct/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rubygarage%2Fapi_struct/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":265521431,"owners_count":23781501,"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","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":["api","ruby","serialization","wrapper"],"created_at":"2024-08-06T08:02:12.370Z","updated_at":"2025-07-16T15:31:40.501Z","avatar_url":"https://github.com/rubygarage.png","language":"Ruby","readme":"# \u003cimg src='https://github.com/rubygarage/api_struct/blob/master/api_struct.svg' height='60' alt='ApiStruct' /\u003e\n\n**ApiStruct** consists of two main interfaces: `ApiStruct::Client` and `ApiStruct::Entity`. The `ApiStruct::Client` class is aimed at using the same interface for describing requests to different APIs. The `ApiStruct::Entity` enables you to use *ApiStruct* clients in ORM-like style.\n\n[![Gem Version](https://badge.fury.io/rb/api_struct.svg)](https://badge.fury.io/rb/api_struct)\n![Maintainability](https://api.codeclimate.com/v1/badges/dc07c83ccbcaaebc6c44/maintainability)\n[![CircleCI](https://circleci.com/gh/rubygarage/api_struct/tree/master.svg?style=svg)](https://circleci.com/gh/rubygarage/api_struct/tree/master)\n\n## Installation\n\nAdd this line to your application's Gemfile:\n\n```ruby\ngem 'api_struct'\n```\n\nAnd then execute:\n\n    $ bundle\n\nOr install it yourself as:\n\n    $ gem install api_struct\n\n## Usage\n\nInitialize APIs routes:\n\n```ruby\nApiStruct::Settings.configure do |config|\n  config.endpoints = {\n    first_api: {\n      root: 'http://localhost:3000/api/v1',\n      headers: {\n        'content-type': 'application/json',\n        'Authorization': 'Bearer TOKEN'\n      }\n    },\n    second_api: {\n      root: 'http://localhost:3001/api/v1',\n      params: { token: 'Default token' }\n    }\n  }\nend\n```\n\n# Client\nEndpoint wrapper\n\n```ruby\nclass PostsClient \u003c ApiStruct::Client\n  first_api :posts\n\n  def show(id)\n    get(id)\n  end\n\n  def index\n    get\n  end\n\n  def user_posts(user_id, post_id = nil)\n    get(post_id, prefix: [:users, user_id])\n    # alias:\n    # get(post_id, prefix: '/users/:id', id: user_id)\n  end\n\n  def custom_path(user_id)\n    get(path: 'users_posts/:user_id', user_id: user_id)\n  end\nend\n```\n\nUsage:\n```ruby\nPostsClient.new.get(1) # -\u003e /posts/1\n```\nReturns `Result` [monad](https://dry-rb.org/gems/dry-monads/1.0/result/)\n```ruby\n# =\u003e Success({:id=\u003e1, :title=\u003e\"Post\"})\n```\n\nOther methods from sample:\n```ruby\npost_client = PostsClient.new\n\npost_client.index            # -\u003e /posts\npost_client.user_posts(1)    # -\u003e /users/1/posts\npost_client.user_posts(1, 2) # -\u003e /users/1/posts/2\npost_client.custom_path(1)   # -\u003e /users_posts/1/\n```\n\n\n# Entity\nResponse serializer\n\n```ruby\nclass User \u003c ApiStruct::Entity\n  client_service UsersClient\n\n  client_service AuthorsClient, prefix: true, only: :index\n  # alias:\n  # client_service AuthorsClient, prefix: :prefix, except: :index\n\n  attr_entity :name, :id\nend\n```\n\n```ruby\nclass UsersClient \u003c ApiStruct::Client\n  first_api :users\n\n  def show(id)\n    get(id)\n  end\nend\n```\n\n```ruby\nclass AuthorsClient \u003c ApiStruct::Client\n  first_api :authors\n\n  def index\n    get\n  end\nend\n```\n\nUsage:\n```ruby\nuser = User.show(1)\n# =\u003e {\"id\"=\u003e1, \"name\"=\u003e\"John\"}\n```\n\nCall methods from prefixed clients:\n```ruby\nusers = User.authors_client_index\n# or\n# users = User.prefix_index\n```\n\nResponse serializers with related entities:\n```ruby\nclass Network \u003c ApiStruct::Entity\n  client_service NetworkClient\n\n  attr_entity :name, :id\n  attr_entity :state, \u0026:to_sym\n\n  has_entity :super_admin, as: User\nend\n```\n\n```ruby\nclass NetworkClient \u003c ApiStruct::Client\n  first_api :networks\n\n  def show(id)\n    get(id)\n  end\nend\n```\n\nUsage:\n```ruby\nnetwork = Network.show(1)\n# =\u003e {\"id\"=\u003e1, \"name\"=\u003e\"Main network\", \"state\"=\u003e\"active\", \"super_admin\"=\u003e{\"id\"=\u003e1, \"name\"=\u003e\"John\"}}\n\nnetwork.state\n# =\u003e :active\n```\n\n## Dynamic headers:\n\n```ruby\nclass Auth\n  def self.call\n    # Get a token..\n  end\nend\n```\n\n```ruby\nclass AuthHeaderValue\n  def self.call\n    { \"Authorization\": \"Bearer #{Auth.call}\" }\n  end\nend\n```\n\n```ruby\nclass PostClient \u003c ApiStruct::Client\n  first_api :posts\n\n  def update(id, post_data)\n    put(id, json: post_data, headers: AuthHeaderValue.call)\n  end\nend\n```\n\n## Contributing\n\nBug reports and pull requests are welcome on GitHub at https://github.com/rubygarage/api_struct.\n\n## License\n\nThe gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).\n***\n\u003ca href=\"https://rubygarage.org/\"\u003e\u003cimg src=\"https://rubygarage.s3.amazonaws.com/assets/assets/rg_color_logo_horizontal-919afc51a81d2e40cb6a0b43ee832e3fcd49669d06785156d2d16fd0d799f89e.png\" alt=\"RubyGarage Logo\" width=\"415\" height=\"128\"\u003e\u003c/a\u003e\n\nRubyGarage is a leading software development and consulting company in Eastern Europe. Our main expertise includes Ruby and Ruby on Rails, but we successfully employ other technologies to deliver the best results to our clients. [Check out our portfolio](https://rubygarage.org/portfolio) for even more exciting works!\n","funding_links":[],"categories":["Ruby"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frubygarage%2Fapi_struct","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Frubygarage%2Fapi_struct","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frubygarage%2Fapi_struct/lists"}