{"id":43879227,"url":"https://github.com/getoutreach/goql","last_synced_at":"2026-02-21T00:17:04.465Z","repository":{"id":37103569,"uuid":"370492124","full_name":"getoutreach/goql","owner":"getoutreach","description":"A GraphQL client package written in Go.","archived":false,"fork":false,"pushed_at":"2025-10-28T07:17:13.000Z","size":466,"stargazers_count":19,"open_issues_count":5,"forks_count":2,"subscribers_count":60,"default_branch":"main","last_synced_at":"2026-02-06T21:14:07.168Z","etag":null,"topics":["client-library","go","golang","golang-library","golang-package","graphql"],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/getoutreach.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":".github/CODEOWNERS","security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2021-05-24T21:44:47.000Z","updated_at":"2025-10-28T07:13:18.000Z","dependencies_parsed_at":"2024-06-19T17:12:46.209Z","dependency_job_id":"cebcdb3b-5efc-4151-8092-bb6203cc8263","html_url":"https://github.com/getoutreach/goql","commit_stats":null,"previous_names":[],"tags_count":32,"template":false,"template_full_name":null,"purl":"pkg:github/getoutreach/goql","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/getoutreach%2Fgoql","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/getoutreach%2Fgoql/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/getoutreach%2Fgoql/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/getoutreach%2Fgoql/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/getoutreach","download_url":"https://codeload.github.com/getoutreach/goql/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/getoutreach%2Fgoql/sbom","scorecard":{"id":424516,"data":{"date":"2025-08-11","repo":{"name":"github.com/getoutreach/goql","commit":"850b9d67c39cc4ecea7cf82b6a134d5315382b74"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":4.8,"checks":[{"name":"Code-Review","score":10,"reason":"all changesets reviewed","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#code-review"}},{"name":"Maintained","score":0,"reason":"1 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#maintained"}},{"name":"Packaging","score":-1,"reason":"packaging workflow not detected","details":["Warn: no GitHub/GitLab publishing workflow detected."],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#packaging"}},{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#binary-artifacts"}},{"name":"Dangerous-Workflow","score":10,"reason":"no dangerous workflow patterns detected","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#dangerous-workflow"}},{"name":"Token-Permissions","score":0,"reason":"detected GitHub workflow tokens with excessive permissions","details":["Warn: no topLevel permission defined: .github/workflows/pull_request-shared-actions.yaml:1","Info: no jobLevel write permissions found"],"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#token-permissions"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#cii-best-practices"}},{"name":"Pinned-Dependencies","score":0,"reason":"dependency not pinned by hash detected -- score normalized to 0","details":["Warn: third-party GitHubAction not pinned by hash: .github/workflows/pull_request-shared-actions.yaml:9: update your workflow using https://app.stepsecurity.io/secureworkflow/getoutreach/goql/pull_request-shared-actions.yaml/main?enable=pin","Info:   0 out of   1 third-party GitHubAction dependencies pinned"],"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#pinned-dependencies"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#fuzzing"}},{"name":"Security-Policy","score":0,"reason":"security policy file not detected","details":["Warn: no security policy file detected","Warn: no security file to analyze","Warn: no security file to analyze","Warn: no security file to analyze"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#security-policy"}},{"name":"License","score":10,"reason":"license file detected","details":["Info: project has a license file: LICENSE:0","Info: FSF or OSI recognized license: Apache License 2.0: LICENSE:0"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"name":"Signed-Releases","score":-1,"reason":"no releases found","details":null,"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#signed-releases"}},{"name":"Branch-Protection","score":5,"reason":"branch protection is not maximal on development and all release branches","details":["Info: 'allow deletion' disabled on branch 'main'","Info: 'force pushes' disabled on branch 'main'","Warn: 'branch protection settings apply to administrators' is disabled on branch 'main'","Warn: 'stale review dismissal' is disabled on branch 'main'","Warn: required approving review count is 1 on branch 'main'","Info: codeowner review is required on branch 'main'","Warn: 'last push approval' is disabled on branch 'main'","Warn: no status checks found to merge onto branch 'main'","Info: PRs are required in order to make changes on branch 'main'"],"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#branch-protection"}},{"name":"Vulnerabilities","score":10,"reason":"0 existing vulnerabilities detected","details":null,"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}},{"name":"SAST","score":0,"reason":"SAST tool is not run on all commits -- score normalized to 0","details":["Warn: 0 commits out of 30 are checked with a SAST tool"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}}]},"last_synced_at":"2025-08-19T01:57:37.938Z","repository_id":37103569,"created_at":"2025-08-19T01:57:37.938Z","updated_at":"2025-08-19T01:57:37.938Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29668682,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-20T23:24:07.480Z","status":"ssl_error","status_checked_at":"2026-02-20T23:24:06.202Z","response_time":59,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"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":["client-library","go","golang","golang-library","golang-package","graphql"],"created_at":"2026-02-06T15:09:56.582Z","updated_at":"2026-02-21T00:17:04.438Z","avatar_url":"https://github.com/getoutreach.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# goql\n[![go.dev reference](https://img.shields.io/badge/go.dev-reference-007d9c?logo=go\u0026logoColor=white)](https://pkg.go.dev/github.com/getoutreach/goql)\n[![Generated via Bootstrap](https://img.shields.io/badge/Outreach-Bootstrap-%235951ff)](https://github.com/getoutreach/bootstrap)\n[![Coverage Status](https://coveralls.io/repos/github/getoutreach/goql/badge.svg?branch=main)](https://coveralls.io/github//getoutreach/goql?branch=main)\n\u003c!-- \u003c\u003cStencil::Block(extraBadges)\u003e\u003e --\u003e\n\n\u003c!-- \u003c\u003c/Stencil::Block\u003e\u003e --\u003e\n\nA GraphQL client package written in Go.\n\n## Contributing\n\nPlease read the [CONTRIBUTING.md](CONTRIBUTING.md) document for guidelines on developing and contributing changes.\n\n## High-level Overview\n\n\u003c!-- \u003c\u003cStencil::Block(overview)\u003e\u003e --\u003e\n\ngoql is a GraphQL client library with built-in two-way marshaling support via struct tags. This is key because it allows\nfor strongly typed GraphQL queries as opposed to variables containing a string representation of the query. This also\nfacilitates more advanced features, such as sparse field sets.\n\nFor complete documentation see the generated [pkg.go documentation](https://pkg.go.dev/github.com/getoutreach/goql). For\na complete guide on the struct tag syntax, see the documentation found below under\n[Defining GraphQL Operations](#defining-graphql-operations).\n\n## Installation\n\nIn the root of your project repository (same directory as your `go.mod` and `go.sum` files):\n\n```shell\ngo get github.com/getoutreach/goql\n```\n\nAfter that you should be able to import it anywhere within your project.\n\n## Defining GraphQL Operations\n\nGraphQL operations can be defined by using normal Go struct types along with the help of struct tags. For example:\n\n```go\ntype QueryUserCollection struct {\n\tUserCollection struct {\n\t\tCollection []struct {\n\t\t\tID   string\n\t\t\tName string\n\t\t} `goql:\"keep\"`\n\t} `goql:\"userCollection(filter:$filter\u003c[Filter]\u003e,sort:$sort\u003c[String]\u003e,size:$size\u003cInt\u003e,before:$before\u003cString\u003e,after:$after\u003cString\u003e)\"`\n}\n```\n\nwhen passed through the GraphQL query marshaller renders the following string:\n\n```graphql\nquery (\n  $filter: [Filter]\n  $sort: [String]\n  $size: Int\n  $before: String\n  $after: String\n) {\n  userCollection(\n    filter: $filter\n    sort: $sort\n    size: $size\n    before: $before\n    after: $after\n  ) {\n    collection {\n      id\n      name\n    }\n  }\n}\n```\n\nHere's the high-level steps to go through when first defining a GraphQL operation:\n\n1. Create a struct that will act as a wrapper for the entire operation. The top-level model will be the only immediate\n   child struct field of this wrapper struct (e.g. `QueryUserCollection`'s only immediate child is `UserCollection` which\n   together represents the `query($filter: [Filter], ...) { userCollection(filter: $filter, ...) { ... } }` part of the\n   output).\n2. Define all of the fields and sub-models of the top-level model as struct fields within the top-level model (e.g.\n   `UserCollection` contains children fields `[]Collection`, `ID`, and `Name`). All types should match the types described\n   in the schema of the query. - `ID` in GraphQL is a `string` in Go. - Any type with the non-null (`!`) restriction in GraphQL should be a non-pointer type in Go. Conversely, any type\n   in GraphQL without this restriction should be nullable (a pointer type) in Go. - If the field is an integral part of the operation, e.g. `UserCollection`, and `Collection` fields in the struct\n   above, add the `goql:\"keep\"` tag to them to tell the marshaler to always include these fields. This is necessary\n   in order for sparse field sets to work. However, in the example above the keep tag can actually be omitted from the\n   `UserCollection` part of the query as it already defines an operation declaration, which the marshaler already sees\n   as an integral part of the operation and implicitly marks it to be kept (that is why the `keep` tag is left off of\n   that portion, but on `Collection` still).\n3. Iterate through the fields and add `goql` struct tags to further define the structure of the operation by\n   modifying declarations, adding aliases, variables, or directives to each field. See the immediately proceeding section,\n   [GraphQL Struct Tag Syntax](#graphql-struct-tag-syntax), for more information on these struct tags and how to define\n   them.\n\n### GraphQL Struct Tag Syntax\n\nThe following components can be used alone or together, separated by a comma within in the tag, to define a `goql`\nstruct tag for a field or model on an operation:\n\n- `modelName(arg:$var\u003cType\u003e, arg2:$var2\u003cType2!\u003e, ...)`\n  - Defines the name and argument list for a model. This is close to what you would see in a normal GraphQL operation,\n    with a little syntactic sugar added to define the types of variables since they're needed in the wrapper of the\n    operation when defining the variables used throughout it. This component implicitly defines the keep tag for the\n    field as well, given that operation declarations are necessary regardless of sparse fieldset instructions.\n  - `` MyModel struct `goql:\"myModel(page:$page\u003cInt!\u003e)\"` `` -\u003e `query($page: Int!) { myModel(page: $page) { ...`\n- `fieldNameOverride`\n  - Overrides the name of a field, by default the lower camel-case version of the name of the struct field is used.\n  - `` Name string `goql:\"username\"` `` -\u003e `username`\n- `@alias(desiredAlias)`\n  - Adds an alias for a field or model, which will change the returned key in the JSON response from the GraphQL\n    server. See [the GraphQL documentation on aliases](https://graphql.org/learn/queries/#aliases) for more information.\n  - An alias is required when an operation name set by a goql tag diverges from the struct field name. Without an\n    alias in that situation the data would not be able to be marshaled back into the struct field after the operation\n    succeeds, resulting in a silent \"error\". As an example, `` Role *Role `goql:\"createRole(...)\"` `` would need an\n    alias since createRole (operation name) != Role (struct field name).\n  - `` Name string `goql:\"@alias(username)\"` `` -\u003e `username: name`\n- `@include($flag)`\n  - Adds an include directive to the field or model. See\n    [the GraphQL documentation on directives](https://graphql.org/learn/queries/#directives) for more information. Note\n    that the variable passed to this directive in the struct tag does not have a type proceeding it in square brackets.\n    This is because these directive variables always have the type of `Boolean!`, so it is implied and therefore not\n    necessary.\n  - `` Name string `goql:\"@include($withName)\"` `` -\u003e `name @include(if: $withName)`\n- `@skip($flag)`\n  - Adds a skip directive to the field or model. See\n    [the GraphQL documentation on directives](https://graphql.org/learn/queries/#directives) for more information. Note\n    that the variable passed to this directive in the struct tag does not have a type proceeding it in square brackets.\n    This is because these directive variables always have the type of `Boolean!`, so it is implied and therefore not\n    necessary.\n  - `` Name string `goql:\"@skip($withoutName)\"` `` -\u003e `name @skip(if: $withoutName)`\n- `keep`\n  - Tells the marshaler to keep this field regardless of what is requested in terms of sparse field sets.\n\nHere is an example of using multiple struct tags together:\n\n`` Name string `goql:\"@alias(username),@include($withName)\"` `` -\u003e `username: name @include(if: $withName)`\n\nRules:\n\n- The same component cannot be defined more than once in a singular struct tag.\n  - `` Name string `goql:\"@include($withName),@include($withName2)\"` `` would result in an error because an include\n    directive was defined twice on the same struct tag.\n- All defined variables must only have one type each associated with them.\n  - `` MyModel struct `goql:\"myModel(page:$page\u003cInt!\u003e,pageSize:$page\u003cInt\u003e)\"` `` would result in an error, since\n    $page is defined to have both the type of `Int!` and `Int`.\n  - `` MyModel struct `goql:\"myModel(page:$page\u003cInt!\u003e),@include($page)\"` `` would also result in an error, since\n    $page is defined to have the type of both `Int!` and `Boolean!` (implicit when used in the include directive).\n\n\u003c!-- \u003c\u003c/Stencil::Block\u003e\u003e --\u003e\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgetoutreach%2Fgoql","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fgetoutreach%2Fgoql","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgetoutreach%2Fgoql/lists"}