{"id":19228945,"url":"https://github.com/romshark/gqlhash","last_synced_at":"2025-04-11T00:13:04.852Z","repository":{"id":260380090,"uuid":"881129908","full_name":"romshark/gqlhash","owner":"romshark","description":"A GraphQL hasher to compare queries without formatting diffs and comments.","archived":false,"fork":false,"pushed_at":"2025-02-01T13:28:11.000Z","size":67,"stargazers_count":5,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-04-11T00:12:56.335Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Go","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/romshark.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,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2024-10-31T00:44:49.000Z","updated_at":"2025-02-01T13:28:14.000Z","dependencies_parsed_at":null,"dependency_job_id":"b5e6284a-59fe-486a-b822-0ae13a494993","html_url":"https://github.com/romshark/gqlhash","commit_stats":null,"previous_names":["romshark/gqlhash"],"tags_count":9,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/romshark%2Fgqlhash","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/romshark%2Fgqlhash/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/romshark%2Fgqlhash/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/romshark%2Fgqlhash/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/romshark","download_url":"https://codeload.github.com/romshark/gqlhash/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248317730,"owners_count":21083530,"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":[],"created_at":"2024-11-09T15:31:10.648Z","updated_at":"2025-04-11T00:13:04.834Z","avatar_url":"https://github.com/romshark.png","language":"Go","funding_links":[],"categories":["Tools"],"sub_categories":["Tools - Miscellaneous"],"readme":"[![GoReportCard](https://goreportcard.com/badge/github.com/romshark/gqlhash)](https://goreportcard.com/report/github.com/romshark/gqlhash)\n[![Coverage Status](https://coveralls.io/repos/github/romshark/gqlhash/badge.svg?branch=main)](https://coveralls.io/github/romshark/gqlhash?branch=main)\n![License](https://img.shields.io/github/license/romshark/gqlhash)\n\n[![GitHub release (latest by date)](https://img.shields.io/github/v/release/romshark/gqlhash)](https://github.com/romshark/gqlhash/releases)\n[![Awesome GraphQL](https://img.shields.io/badge/Awesome-GraphQL-%23e535ab?logo=graphql\u0026logoColor=white)](https://github.com/chentsulin/awesome-graphql?tab=readme-ov-file#tools---miscellaneous)\n[![GoDoc](https://godoc.org/github.com/romshark/gqlhash?status.svg)](https://pkg.go.dev/github.com/romshark/gqlhash)\n\n# gqlhash\n\nGenerates SHA1 ([and other](#hash-function)) hashes from GraphQL\n[executable documents](https://spec.graphql.org/October2021/#sec-Executable-Definitions)\nignoring formatting and comment diffs to enable fast and robust hash-based comparisons.\n\ngqlhash is [significantly faster](#performance) ⚡ than parsing query documents and\ncomparing the ASTs or comparing documents after minification.\nIt can be used to efficiently check whether a GraphQL query is in a set of\n[trusted documents](https://benjie.dev/graphql/trusted-documents) by hash.\n\nThe following two documents will generate the same SHA1 hash despite the\ndifference in formatting and comments:\n\n```graphql\n{\n  object(x: 42, y: 1.0) {\n    id\n    name\n    description @translate(lang: [DE, EN])\n    blockstring(s: \"\"\"gqlhash parses block string values\n      and doesn't care about formatting.\"\"\")\n  }\n}\n```\n\n```graphql\nquery {\n  # Some comment\n  object(x: 42, y: 1.0) {\n    id\n    name # We will need this.\n    description\n      @translate(\n        lang: [DE, EN] # Prefer German, if possible.\n      )\n    blockstring(\n      s: \"\"\"\n      gqlhash parses block string values\n      and doesn't care about formatting.\n      \"\"\"\n    )\n  }\n}\n```\n\ngqlhash is fully compliant with the latest GraphQL specification of\n[October 2021](https://spec.graphql.org/October2021/).\n\n## Installation\n\n### Homebrew 🍺\n\n```sh\nbrew tap romshark/tools\nbrew install gqlhash\n```\n\n### Compiled Binary\n\nDownload one of the compiled binaries from\n[GitHub Releases](https://github.com/romshark/gqlhash/releases).\n\n### From Source\n\n```sh\ngo install github.com/romshark/gqlhash@latest\n```\n\nThis requires the latest version of [Go](https://go.dev) to be installed.\n\n## Usage\n\ngqlhash can read the GraphQL query from stdin until EOF and\nprint the resulting SHA1 hash as hexadecimal string to stdout:\n\n```sh\n# prints: fa8eb9872f835fc36f89e20e762516510622aba8\necho '{foo bar}' | gqlhash\n```\n\nTo print the version of gqlhash, use:\n\n```sh\ngqlhash -version\n```\n\n### File Input\n\ngqlhash can also read from a file provided via `-file` if necessary:\n\n```sh\ngqlhash -file ./executable_document.graphql\n```\n\n### Output Format\n\ngqlhash supports the following output formats:\n\n- `hex` (hexadecimal string)\n- `base32` (base32 encoding as defined in\n  [RFC 4648](https://datatracker.ietf.org/doc/html/rfc4648))\n- `base64` (base64 encoding as defined in\n  [RFC 4648](https://datatracker.ietf.org/doc/html/rfc4648))\n\nBy default `hex` is used. Use `-format` to specify a different hash function:\n\n```sh\n# prints: +o65hy+DX8NvieIOdiUWUQYiq6g=\necho '{foo bar}' | gqlhash -format base64\n```\n\n### Hash Function\n\ngqlhash supports multiple common hash functions:\n\n- `sha1`\n- `sha2` (SHA-256)\n- `sha3` (SHA3-512)\n- `md5`\n- `blake2b` (unkeyed)\n- `blake2s` (unkeyed)\n- `fnv`\n- `crc32` (IEEE polynomial)\n- `crc64` (ISO polynomial, defined in ISO 3309)\n\nBy default `sha1` is used. Use `-hash` to specify a different hash function:\n\n```sh\n# prints: t2XWfakQNusOObQfnS09PT3NOgfVqFOyizwqxYzxn4k=\necho '{foo bar}' | gqlhash -hash sha2 -format base64\n```\n\n## Performance\n\n- Compared to plain SHA1 hashing gqlhash performance overhead is just **~5x**\n  on average across benchmarks (min: ~3x, max: ~7x).\n- Compared to parsing the queries into AST with\n  [vektah/gqlparser/v2](https://github.com/vektah/gqlparser).\n  gqlhash shows a significant advantage of **~15x**\n  on average across benchmarks (min: ~10x; max: ~25x).\n  Also, gqlhash **doesn't allocate memory** dynamically at all, compared to\n  hundrets of allocations for the same queries by gqlparser.\n\nSee benchmark results below.\n\n\u003cdetails\u003e\n\n```\ngoos: darwin\ngoarch: arm64\npkg: github.com/romshark/gqlhash\ncpu: Apple M1 Max\nBenchmarkReferenceSHA1/blockstring/minified/direct-10           15573957                76.85 ns/op            0 B/op          0 allocs/op\nBenchmarkReferenceSHA1/blockstring/minified/gqlhash-10           3062020               392.5 ns/op             0 B/op          0 allocs/op\nBenchmarkReferenceSHA1/blockstring/minified/vektah-10             206655              5511 ns/op            7105 B/op        156 allocs/op\n\nBenchmarkReferenceSHA1/blockstring/formatted/direct-10          15431370                77.38 ns/op            0 B/op          0 allocs/op\nBenchmarkReferenceSHA1/blockstring/formatted/gqlhash-10          2743230               436.9 ns/op             0 B/op          0 allocs/op\nBenchmarkReferenceSHA1/blockstring/formatted/vektah-10            202897              5613 ns/op            7153 B/op        156 allocs/op\n\nBenchmarkReferenceSHA1/tiny/minified/direct-10                  21461752                55.36 ns/op            0 B/op          0 allocs/op\nBenchmarkReferenceSHA1/tiny/minified/gqlhash-10                  7236796               164.3 ns/op             0 B/op          0 allocs/op\nBenchmarkReferenceSHA1/tiny/minified/vektah-10                    279013              4060 ns/op            5601 B/op        133 allocs/op\n\nBenchmarkReferenceSHA1/tiny/formatted/direct-10                 21669319                55.03 ns/op            0 B/op          0 allocs/op\nBenchmarkReferenceSHA1/tiny/formatted/gqlhash-10                 6503784               183.8 ns/op             0 B/op          0 allocs/op\nBenchmarkReferenceSHA1/tiny/formatted/vektah-10                   278722              4067 ns/op            5601 B/op        133 allocs/op\n\nBenchmarkReferenceSHA1/medium/minified/direct-10                 9457255               128.0 ns/op             0 B/op          0 allocs/op\nBenchmarkReferenceSHA1/medium/minified/gqlhash-10                1441172               830.6 ns/op             0 B/op          0 allocs/op\nBenchmarkReferenceSHA1/medium/minified/vektah-10                  102486             11350 ns/op           13321 B/op        246 allocs/op\n\nBenchmarkReferenceSHA1/medium/formatted/direct-10                5762872               207.9 ns/op             0 B/op          0 allocs/op\nBenchmarkReferenceSHA1/medium/formatted/gqlhash-10               1000000              1059 ns/op               0 B/op          0 allocs/op\nBenchmarkReferenceSHA1/medium/formatted/vektah-10                  94951             12437 ns/op           13937 B/op        261 allocs/op\n\nBenchmarkReferenceSHA1/big/minified/direct-10                    1445761               828.1 ns/op             0 B/op          0 allocs/op\nBenchmarkReferenceSHA1/big/minified/gqlhash-10                    253197              4678 ns/op               0 B/op          0 allocs/op\nBenchmarkReferenceSHA1/big/minified/vektah-10                      22827             52391 ns/op           49096 B/op        798 allocs/op\n\nBenchmarkReferenceSHA1/big/formatted/direct-10                    989251              1195 ns/op               0 B/op          0 allocs/op\nBenchmarkReferenceSHA1/big/formatted/gqlhash-10                   210759              5661 ns/op               0 B/op          0 allocs/op\nBenchmarkReferenceSHA1/big/formatted/vektah-10                     21392             55751 ns/op           50615 B/op        836 allocs/op\nPASS\nok      github.com/romshark/gqlhash     34.615s\n```\n\n\u003c/details\u003e\n\n## Known Limitations\n\n### Order of Operations, Selections and Arguments\n\ngqlhash ignores **irrelevant differences** between documents such as formatting\nand comments, but it will return different hashes for queries with different\norder of operations, selections and arguments despite them being identical in content.\n**This is by design** to allow for fast hashing and reduced code complexity.\n\n### Strings \u0026 Block Strings\n\nIn theory you'd assume the following two queries should result in the same hash:\n\n```graphql\n{\n  blockstring(\n    s: \"\"\"\n    line 1\n    line 2\n    \"\"\"\n  )\n}\n```\n\n```graphql\n{\n  blockstring(\n    s: \"line 1\\nline 2\"\n  )\n}\n```\n\nBut they won't because even though the string values are identical, the former uses\na block string while the latter isn't.\nIn the case when gqlhash is used for query allowlisting\n(a.k.a. [Trusted Documents](https://benjie.dev/graphql/trusted-documents))\nwe usually don't want variantions to be allowed, instead we just want the irrelevant\nformatting and comments to be ignored.\nWhether strings and block strings with equal value should result in the same hash\nis up for debate and should probably be configurable via CLI flag.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fromshark%2Fgqlhash","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fromshark%2Fgqlhash","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fromshark%2Fgqlhash/lists"}