{"id":13413705,"url":"https://github.com/ccbrown/api-fu","last_synced_at":"2026-02-07T23:38:00.688Z","repository":{"id":41983050,"uuid":"199579747","full_name":"ccbrown/api-fu","owner":"ccbrown","description":"A collection of Go packages for creating robust GraphQL APIs","archived":false,"fork":false,"pushed_at":"2024-08-29T04:34:58.000Z","size":655,"stargazers_count":57,"open_issues_count":3,"forks_count":4,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-12-23T08:46:13.692Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","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/ccbrown.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":"2019-07-30T05:18:43.000Z","updated_at":"2025-06-18T16:28:25.000Z","dependencies_parsed_at":"2024-06-19T00:21:53.308Z","dependency_job_id":"b8fe8e7b-9fe2-487e-ae4c-df4cba929400","html_url":"https://github.com/ccbrown/api-fu","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/ccbrown/api-fu","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ccbrown%2Fapi-fu","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ccbrown%2Fapi-fu/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ccbrown%2Fapi-fu/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ccbrown%2Fapi-fu/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ccbrown","download_url":"https://codeload.github.com/ccbrown/api-fu/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ccbrown%2Fapi-fu/sbom","scorecard":{"id":268940,"data":{"date":"2025-08-11","repo":{"name":"github.com/ccbrown/api-fu","commit":"b02009cb8d793d8d45a9b310c82d122db3629a67"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":2.8,"checks":[{"name":"Maintained","score":0,"reason":"0 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":"Code-Review","score":0,"reason":"Found 2/30 approved changesets -- score normalized to 0","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":"Token-Permissions","score":0,"reason":"detected GitHub workflow tokens with excessive permissions","details":["Warn: no topLevel permission defined: .github/workflows/build.yml: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":"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":"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":"Pinned-Dependencies","score":0,"reason":"dependency not pinned by hash detected -- score normalized to 0","details":["Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/build.yml:9: update your workflow using https://app.stepsecurity.io/secureworkflow/ccbrown/api-fu/build.yml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/build.yml:14: update your workflow using https://app.stepsecurity.io/secureworkflow/ccbrown/api-fu/build.yml/main?enable=pin","Warn: third-party GitHubAction not pinned by hash: .github/workflows/build.yml:26: update your workflow using https://app.stepsecurity.io/secureworkflow/ccbrown/api-fu/build.yml/main?enable=pin","Info:   0 out of   2 GitHub-owned GitHubAction dependencies pinned","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":"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":"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":"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":"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":"License","score":10,"reason":"license file detected","details":["Info: project has a license file: LICENSE:0","Info: FSF or OSI recognized license: MIT License: 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":-1,"reason":"internal error: error during branchesHandler.setup: internal error: githubv4.Query: Resource not accessible by integration","details":null,"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":"SAST","score":0,"reason":"SAST tool is not run on all commits -- score normalized to 0","details":["Warn: 0 commits out of 19 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"}},{"name":"Vulnerabilities","score":0,"reason":"25 existing vulnerabilities detected","details":["Warn: Project is vulnerable to: GO-2022-0229 / GHSA-cjjc-xp8v-855w","Warn: Project is vulnerable to: GO-2020-0012 / GHSA-ffhg-7mh4-33c4","Warn: Project is vulnerable to: GO-2021-0227 / GHSA-3vm4-22fp-5rfm","Warn: Project is vulnerable to: GO-2022-0968 / GHSA-gwc9-m7rh-j2ww","Warn: Project is vulnerable to: GO-2021-0356 / GHSA-8c26-wmh5-6g9v","Warn: Project is vulnerable to: GO-2024-2961","Warn: Project is vulnerable to: GO-2023-2402 / GHSA-45x7-px36-x8w8","Warn: Project is vulnerable to: GO-2024-3321 / GHSA-v778-237x-gjrc","Warn: Project is vulnerable to: GO-2025-3487 / GHSA-hcg3-q754-cr77","Warn: Project is vulnerable to: GO-2022-0536 / GHSA-39qc-96h7-956f / GHSA-hgr8-6h9x-f7q9","Warn: Project is vulnerable to: GO-2022-0236 / GHSA-h86h-8ppg-mxmh","Warn: Project is vulnerable to: GO-2021-0238 / GHSA-83g2-8m93-v3w7","Warn: Project is vulnerable to: GO-2022-0288","Warn: Project is vulnerable to: GO-2022-0969 / GHSA-69cg-p879-7622","Warn: Project is vulnerable to: GO-2022-1144 / GHSA-xrjj-mj9h-534m","Warn: Project is vulnerable to: GO-2023-1571 / GHSA-vvpx-j8f3-3w6h","Warn: Project is vulnerable to: GO-2023-1988 / GHSA-2wrh-6pvc-2jm9","Warn: Project is vulnerable to: GO-2023-2102 / GHSA-4374-p667-p6c8","Warn: Project is vulnerable to: GHSA-qppj-fm5r-hxr3","Warn: Project is vulnerable to: GO-2024-2687 / GHSA-4v7x-pqxf-cx7m","Warn: Project is vulnerable to: GO-2024-3333","Warn: Project is vulnerable to: GO-2025-3503 / GHSA-qxp5-gwg8-xv66","Warn: Project is vulnerable to: GO-2025-3595 / GHSA-vvgc-356p-c3xw","Warn: Project is vulnerable to: GO-2022-0942 / GHSA-h3qm-jrrf-cgj3","Warn: Project is vulnerable to: GO-2024-2611 / GHSA-8r3f-844c-mc37"],"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}}]},"last_synced_at":"2025-08-17T12:52:37.139Z","repository_id":41983050,"created_at":"2025-08-17T12:52:37.139Z","updated_at":"2025-08-17T12:52:37.139Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29212759,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-07T23:36:15.537Z","status":"ssl_error","status_checked_at":"2026-02-07T23:36:12.879Z","response_time":63,"last_error":"SSL_read: 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":[],"created_at":"2024-07-30T20:01:46.952Z","updated_at":"2026-02-07T23:38:00.672Z","avatar_url":"https://github.com/ccbrown.png","language":"Go","readme":"# api-fu ![GitHub Actions](https://github.com/ccbrown/api-fu/workflows/Build/badge.svg) [![Go Report Card](https://goreportcard.com/badge/github.com/ccbrown/api-fu)](https://goreportcard.com/report/github.com/ccbrown/api-fu) [![codecov](https://codecov.io/gh/ccbrown/api-fu/branch/master/graph/badge.svg)](https://codecov.io/gh/ccbrown/api-fu) [![Documentation](https://godoc.org/github.com/ccbrown/api-fu?status.svg)](https://godoc.org/github.com/ccbrown/api-fu) [![Mentioned in Awesome Go](https://awesome.re/mentioned-badge.svg)](https://github.com/avelino/awesome-go)\n\n**api-fu** (noun)\n  1. (informal) Mastery of APIs. 💪\n\n## Packages\n\n* The top level `apifu` package is an opinionated library that aims to make it as easy as possible to build APIs that conform to API-fu's ideals. See the examples directory for example usage.\n* The `graphql` package is an unopinionated library for building GraphQL APIs. If you agree with API-fu's ideals, you should use `apifu` instead, but if you want something lower level, the `graphql` package is still an excellent standalone GraphQL library. It fully supports all features of the [June 2018 spec](https://graphql.github.io/graphql-spec/June2018/).\n* The `graphql/transport` directory contains unopinionated libraries for using various transport protocols. For example, it contains transport implementations that allow you to serve your GraphQL API via WebSockets and provide subscription functionality.\n* The `cmd/gql-client-gen` package provides a CLI tool that can be used to generate types for use by client code. THis allows your client's queries to be type-safe and fully validated at compile time.\n\n### Experimental Packages\n\nThe above packages are mature and have been thoroughly proven in real-world, production deployments. The following packages have not yet seen such rigorous real-world testing and are thus considered experimental. They are fully functional and well unit tested, but may change at any time and are not yet subject to any compatibility guarantees.\n\n* The `jsonapi` package is a library for building [JSON:API](https://jsonapi.org) APIs. It's somewhat high level, but is no more opinionated than JSON:API itself is. However, it does hold some of those opinions more strongly (i.e. it doesn't support violating many of the JSON:API spec's recommendations and \"SHOULD\"s).\n\n## Usage\n\nAPI-fu builds GraphQL APIs with code. To begin, you need a config that at least defines a query field:\n\n```go\nvar fuCfg apifu.Config\n\nfuCfg.AddQueryField(\"foo\", \u0026graphql.FieldDefinition{\n    Type: graphql.StringType,\n    Resolve: func(ctx *graphql.FieldContext) (interface{}, error) {\n        return \"bar\", nil\n    },\n})\n```\n\nFrom there, you can build the API:\n\n```go\nfu, err := apifu.NewAPI(\u0026fuCfg)\nif err != nil {\n    panic(err)\n}\n```\n\nAnd serve it:\n\n```go\nfu.ServeGraphQL(w, r)\n```\n\nAPI-fu also has first-class support for common patterns such as nodes that are queryable using global ids. See the examples directory for more complete example code.\n\n## Features\n\n### ✅ Supports all features of the [latest GraphQL spec](https://spec.graphql.org/June2018/).\n\nThis includes null literals, error extensions, subscriptions, and directives.\n\n### 🚅 Fast!\n\nThe graphql package is over twice as fast and several times more memory efficient than its inspiration ([graphql-go/graphql](https://github.com/graphql-go/graphql)).\n\n```\npkg: github.com/ccbrown/api-fu/graphql/benchmarks\nBenchmarkAPIFu\nBenchmarkAPIFu-16        \t     765\t   1553517 ns/op\t  890575 B/op\t   22587 allocs/op\nBenchmarkGraphQLGo\nBenchmarkGraphQLGo-16    \t     315\t   3753681 ns/op\t 3990220 B/op\t   45952 allocs/op\n```\n\n### ⚡️ Supports efficient batching and concurrency without the use of goroutines.\n\nThe `graphql` package supports virtually any batching or concurrency pattern using low level primitives.\n\nThe `apifu` package provides high level ways to use them.\n\nFor example, you can define a resolver like this to do work in a goroutine:\n\n```go\nfuCfg.AddQueryField(\"myField\", \u0026graphql.FieldDefinition{\n    Type: graphql.IntType,\n    Resolve: func(ctx *graphql.FieldContext) (interface{}, error) {\n        return Go(ctx.Context, func() (interface{}, error) {\n            return doSomethingComplex(), nil\n        }), nil\n    },\n})\n```\n\nOr you can define a resolver like this to batch up queries, allowing you to minimize round trips to your database:\n\n```go\nfuCfg.AddQueryField(\"myField\", \u0026graphql.FieldDefinition{\n    Type: graphql.IntType,\n    Resolve: Batch(func(ctx []*graphql.FieldContext) []graphql.ResolveResult {\n        return resolveABunchOfTheseAtOnce(ctx)\n    },\n})\n```\n\n### 💡 Provides implementations for commonly used scalar types.\n\nFor example, the `apifu` package provides date-time and long (but JavaScript safe) integers.\n\n### 📡 Implements handlers for HTTP, the [Apollo graphql-ws protocol](https://github.com/apollographql/subscriptions-transport-ws), and the [newer graphql-transport-ws protocol](https://github.com/enisdenjo/graphql-ws).\n\nOnce you've built your API, all you have to do is:\n\n```go\nfu.ServeGraphQL(w, r)\n```\n\nOr:\n\n```go\nfu.ServeGraphQLWS(w, r)\n```\n\n### 📖 Provides easy-to-use helpers for creating connections adhering to the [Relay Cursor Connections Specification](https://facebook.github.io/relay/graphql/connections.htm).\n\nJust provide a name, cursor constructor, edge fields, and edge getter:\n\n```go\n{\n    \"messagesConnection\": apifu.TimeBasedConnection(\u0026apifu.TimeBasedConnectionConfig{\n        NamePrefix: \"ChannelMessages\",\n        EdgeCursor: func(edge interface{}) apifu.TimeBasedCursor {\n            message := edge.(*model.Message)\n            return apifu.NewTimeBasedCursor(message.Time, string(message.Id))\n        },\n        EdgeFields: map[string]*graphql.FieldDefinition{\n            \"node\": \u0026graphql.FieldDefinition{\n                Type: graphql.NewNonNullType(messageType),\n                Resolve: func(ctx *graphql.FieldContext) (interface{}, error) {\n                    return ctx.Object, nil\n                },\n            },\n        },\n        EdgeGetter: func(ctx *graphql.FieldContext, minTime time.Time, maxTime time.Time, limit int) (interface{}, error) {\n            return ctxSession(ctx.Context).GetMessagesByChannelIdAndTimeRange(ctx.Object.(*model.Channel).Id, minTime, maxTime, limit)\n        },\n    }),\n}\n```\n\n### 🛠 Can generate Apollo-like client-side type definitions and validate queries in source code.\n\nThe `gql-client-gen` tool can be used to generate types for use in client-side code as well as validate queries at compile-time. The generated types intelligently unmarshal inline fragments and fragment spreads based on `__typename` values.\n\nSee [cmd/gql-client-gen](cmd/gql-client-gen) for details.\n\n### 🚔 Calculates operation costs during validation for rate limiting and metering\n\nDuring validation, you can specify a max operation cost or get the actual cost\nof an operation using customizable cost definitions:\n\n```go\ndoc, errs := graphql.ParseAndValidate(req.Query, req.Schema, req.ValidateCost(maxCost, \u0026actualCost))\n```\n\n## API Design Guidelines\n\nThe following are guidelines that are recommended for all new GraphQL APIs. API-fu aims to make it easy to conform to these for robust and future-proof APIs:\n\n* All mutations should resolve to result types. No mutations should simply resolve to a node. For example, a `createUser` mutation should resolve to a `CreateUserResult` object with a `user` field rather than simply resolving to a `User`. This is necessary to keep mutations extensible. Likewise, subscriptions should not resolve directly to node types. For example, a subscription for messages in a chat room (`chatRoomMessages`) should resolve to a `ChatRoomMessagesEvent` type.\n* Nodes with 1-to-many relationships should make related nodes available via [Relay Cursor Connections](https://facebook.github.io/relay/graphql/connections.htm). Nodes should not have fields that simply resolve to lists of related nodes. Additionally, all connections must require a `first` or `last` argument that specifies the upper bound on the number of nodes returned by that connection. This makes it possible to determine an upper bound on the number of nodes returned by a query before that query begins execution, e.g. using rules similar to [GitHub's](https://developer.github.com/v4/guides/resource-limitations/).\n* Mutations that modify nodes should always include the updated version of that node in the result. This makes it easy for clients to maintain up-to-date state and tolerate eventual consistency (If a client updates a resource, then immediately requests it in a subsequent query, the server may provide a version of the resource that was cached before the update.).\n* Nodes should provide revision numbers. Each time a node is modified, the revision number must increment. This helps clients maintain up-to-date state and enables simultaneous change detection.\n* It should be easy for clients to query historical data and subscribe to real-time data without missing anything due to race conditions. The most transparent and fool-proof way to facilitate this is to make subscriptions immediately push a small history of events to clients as soon as they're started. The pushed history should generally only need to cover a few seconds' worth of events. If queries use eventual consistency, the pushed history should be at least as large as the query cache's TTL.\n\n## Versioning and Compatibility Guarantees\n\nThis library is not versioned. However, one guarantee is made: Unless otherwise noted, backwards-incompatible changes made will break your build at compile-time. If your application compiles after updating API-fu, you're good to go.\n","funding_links":[],"categories":["Query Language","Go","查询语言","Go语言包管理","Relational Databases"],"sub_categories":["HTTP Clients","HTTP客户端","查询语"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fccbrown%2Fapi-fu","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fccbrown%2Fapi-fu","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fccbrown%2Fapi-fu/lists"}