{"id":24922328,"url":"https://github.com/mbuhot/json_api_query_builder","last_synced_at":"2025-04-09T18:43:16.048Z","repository":{"id":57509892,"uuid":"113710858","full_name":"mbuhot/json_api_query_builder","owner":"mbuhot","description":"Build Ecto queries from JSON-API requests","archived":false,"fork":false,"pushed_at":"2020-10-10T07:03:51.000Z","size":24,"stargazers_count":11,"open_issues_count":0,"forks_count":0,"subscribers_count":4,"default_branch":"master","last_synced_at":"2025-03-23T20:43:46.377Z","etag":null,"topics":["ecto","elixir","json-api"],"latest_commit_sha":null,"homepage":null,"language":"Elixir","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/mbuhot.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2017-12-10T00:15:24.000Z","updated_at":"2024-12-06T00:40:09.000Z","dependencies_parsed_at":"2022-09-26T17:51:21.614Z","dependency_job_id":null,"html_url":"https://github.com/mbuhot/json_api_query_builder","commit_stats":null,"previous_names":[],"tags_count":3,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mbuhot%2Fjson_api_query_builder","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mbuhot%2Fjson_api_query_builder/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mbuhot%2Fjson_api_query_builder/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mbuhot%2Fjson_api_query_builder/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/mbuhot","download_url":"https://codeload.github.com/mbuhot/json_api_query_builder/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248090421,"owners_count":21046089,"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":["ecto","elixir","json-api"],"created_at":"2025-02-02T11:18:55.745Z","updated_at":"2025-04-09T18:43:16.026Z","avatar_url":"https://github.com/mbuhot.png","language":"Elixir","funding_links":[],"categories":[],"sub_categories":[],"readme":"[![Build Status](https://img.shields.io/travis/mbuhot/json_api_query_builder.svg?branch=master)](https://travis-ci.org/mbuhot/json_api_query_builder)\n[![Hex.pm](https://img.shields.io/hexpm/v/json_api_query_builder.svg)](https://hex.pm/packages/json_api_query_builder)\n[![HexDocs](https://img.shields.io/badge/api-docs-yellow.svg)](https://hexdocs.pm/json_api_query_builder/)\n[![Inch CI](http://inch-ci.org/github/mbuhot/json_api_query_builder.svg)](http://inch-ci.org/github/mbuhot/json_api_query_builder)\n[![License](https://img.shields.io/hexpm/l/json_api_query_builder.svg)](https://github.com/mbuhot/json_api_query_builder/blob/master/LICENSE)\n\n# JSON-API Query Builder\n\nBuild Ecto queries from JSON-API requests.\n\nDocs can be found at [https://hexdocs.pm/json_api_query_builder](https://hexdocs.pm/json_api_query_builder).\n\n## Installation\n\nThe package can be installed by adding `json_api_query_builder` to your list of dependencies in `mix.exs`:\n\n```elixir\ndef deps do\n  [{:json_api_query_builder, \"~\u003e 1.0\"}]\nend\n```\n\n## Features\n\nJSON-API Query Builder can be used to construct an efficient Ecto query to handle the following kinds of requests, in arbitrary combinations.\n\n### Sparse Fieldsets\n\nGet all articles, including only the `title` and `description` fields\n\n`/blog/articles/?fields[article]=title,description`\n\n### Sorting\n\nGet all articles, sorted by `category` ascending and `published` descending\n\n`/blog/articles/?sort=category,-published`\n\n### Included Resources\n\nGet all articles, including related author, comments and comments user\n\n`/blog/articles/?include=author,comments,comments.user`\n\n### Attribute Filters\n\nGet all articles with the animals `tag`\n\n`/blog/articles/?filter[tag]=animals`\n\n### Filter by related resource\n\nGet all users who have an article with the animals `tag`\n\n`/blog/users?filter[article.tag]=animals`\n\n### Filter included resources\n\nGet all users, including related articles that have the animals `tag`\n\n`/blog/users?include=articles\u0026filter[article][tag]=animals`\n\n### Pagination\n\nTODO\n\n## Usage\n\nFor each Ecto schema, create a related query builder module:\n\n```elixir\ndefmodule Article do\n  use Ecto.Schema\n\n  schema \"articles\" do\n    field :body, :string\n    field :description, :string\n    field :slug, :string\n    field :tag_list, {:array, :string}\n    field :title, :string\n    belongs_to :author, User, foreign_key: :user_id\n    has_many :comments, Comment\n    timestamps()\n  end\n\n  defmodule Query do\n    use JsonApiQueryBuilder,\n      schema: Article,\n      type: \"article\",\n      relationships: [\"author\", \"comments\"]\n\n    @impl JsonApiQueryBuilder\n    def filter(query, \"tag\", value), do: from(a in query, where: ^value in a.tag_list)\n    def filter(query, \"comments\", params) do\n      comment_query = from(Comment, select: [:article_id], distinct: true) |\u003e Comment.Query.filter(params)\n      from a in query, join: c in ^subquery(comment_query), on: a.id == c.article_id\n    end\n    def filter(query, \"author\", params) do\n      user_query = from(User, select: [:id]) |\u003e User.Query.filter(params)\n      from a in query, join: u in ^subquery(user_query), on: a.user_id == u.id\n    end\n\n    @impl JsonApiQueryBuilder\n    def include(query, \"comments\", comment_params) do\n      from query, preload: [comments: ^Comment.Query.build(comment_params)]\n    end\n    def include(query, \"author\", author_params) do\n      from query, select_merge: [:author_id], preload: [author: ^User.Query.build(author_params)]\n    end\n  end\nend\n```\n\nThen in an API request handler, use the query builder:\n\n```elixir\ndefmodule ArticleController do\n  use MyAppWeb, :controller\n\n  def index(conn, params) do\n    articles =\n      params\n      |\u003e Article.Query.build()\n      |\u003e MyApp.Repo.all()\n\n    # pass data and opts as expected by `ja_serializer`\n    render(\"index.json-api\", data: articles, opts: [\n      fields: params[\"fields\"],\n      include: params[\"include\"]\n    ])\n  end\nend\n```\n\n## Generated Queries\n\nUsing `join:` queries for filtering based on relationships, `preload:` queries for included resources and `select:` lists for sparse fieldsets, the generated queries are as efficient as what you would write by hand.\n\nEg the following index request:\n\n```elixir\nparams = %{\n  \"fields\" =\u003e %{\n    \"article\" =\u003e \"description\",\n    \"comment\" =\u003e \"body\",\n    \"user\" =\u003e \"email,username\"\n  },\n  \"filter\" =\u003e %{\n    \"articles.tag\" =\u003e \"animals\"\n  },\n  \"include\" =\u003e \"articles,articles.comments,articles.comments.user\"\n}\nBlog.Repo.all(Blog.User.Query.build(params))\n```\n\nProduces one join query for filtering, and 3 preload queries\n\n```\n[debug] QUERY OK source=\"users\" db=3.8ms decode=0.1ms queue=0.1ms\nSELECT u0.\"email\", u0.\"username\", u0.\"id\"\nFROM \"users\" AS u0\nINNER JOIN (\n  SELECT DISTINCT a0.\"user_id\" AS \"user_id\"\n  FROM \"articles\" AS a0\n  WHERE ($1 = ANY(a0.\"tag_list\"))\n) AS s1\nON u0.\"id\" = s1.\"user_id\" [\"animals\"]\n\n[debug] QUERY OK source=\"articles\" db=1.9ms\nSELECT a0.\"description\", a0.\"id\", a0.\"user_id\"\nFROM \"articles\" AS a0\nWHERE (a0.\"user_id\" = ANY($1))\nORDER BY a0.\"user_id\" [[2, 1]]\n\n[debug] QUERY OK source=\"comments\" db=1.7ms\nSELECT c0.\"body\", c0.\"id\", c0.\"user_id\", c0.\"article_id\"\nFROM \"comments\" AS c0\nWHERE (c0.\"article_id\" = ANY($1))\nORDER BY c0.\"article_id\" [[4, 3, 2, 1]]\n\n[debug] QUERY OK source=\"users\" db=1.3ms\nSELECT u0.\"email\", u0.\"username\", u0.\"id\", u0.\"id\"\nFROM \"users\" AS u0\nWHERE (u0.\"id\" = $1) [2]\n```\n\n## License\n\nMIT\n\n## Contributing\n\nGitHub issues and pull requests welcome.\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmbuhot%2Fjson_api_query_builder","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmbuhot%2Fjson_api_query_builder","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmbuhot%2Fjson_api_query_builder/lists"}