Home

Awesome

api-fu GitHub Actions Go Report Card codecov Documentation Mentioned in Awesome Go

api-fu (noun)

  1. (informal) Mastery of APIs. 💪

Packages

Experimental Packages

The 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.

Usage

API-fu builds GraphQL APIs with code. To begin, you need a config that at least defines a query field:

var fuCfg apifu.Config

fuCfg.AddQueryField("foo", &graphql.FieldDefinition{
    Type: graphql.StringType,
    Resolve: func(ctx *graphql.FieldContext) (interface{}, error) {
        return "bar", nil
    },
})

From there, you can build the API:

fu, err := apifu.NewAPI(&fuCfg)
if err != nil {
    panic(err)
}

And serve it:

fu.ServeGraphQL(w, r)

API-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.

Features

✅ Supports all features of the latest GraphQL spec.

This includes null literals, error extensions, subscriptions, and directives.

🚅 Fast!

The graphql package is over twice as fast and several times more memory efficient than its inspiration (graphql-go/graphql).

pkg: github.com/ccbrown/api-fu/graphql/benchmarks
BenchmarkAPIFu
BenchmarkAPIFu-16        	     765	   1553517 ns/op	  890575 B/op	   22587 allocs/op
BenchmarkGraphQLGo
BenchmarkGraphQLGo-16    	     315	   3753681 ns/op	 3990220 B/op	   45952 allocs/op

⚡️ Supports efficient batching and concurrency without the use of goroutines.

The graphql package supports virtually any batching or concurrency pattern using low level primitives.

The apifu package provides high level ways to use them.

For example, you can define a resolver like this to do work in a goroutine:

fuCfg.AddQueryField("myField", &graphql.FieldDefinition{
    Type: graphql.IntType,
    Resolve: func(ctx *graphql.FieldContext) (interface{}, error) {
        return Go(ctx.Context, func() (interface{}, error) {
            return doSomethingComplex(), nil
        }), nil
    },
})

Or you can define a resolver like this to batch up queries, allowing you to minimize round trips to your database:

fuCfg.AddQueryField("myField", &graphql.FieldDefinition{
    Type: graphql.IntType,
    Resolve: Batch(func(ctx []*graphql.FieldContext) []graphql.ResolveResult {
        return resolveABunchOfTheseAtOnce(ctx)
    },
})

💡 Provides implementations for commonly used scalar types.

For example, the apifu package provides date-time and long (but JavaScript safe) integers.

📡 Implements handlers for HTTP, the Apollo graphql-ws protocol, and the newer graphql-transport-ws protocol.

Once you've built your API, all you have to do is:

fu.ServeGraphQL(w, r)

Or:

fu.ServeGraphQLWS(w, r)

📖 Provides easy-to-use helpers for creating connections adhering to the Relay Cursor Connections Specification.

Just provide a name, cursor constructor, edge fields, and edge getter:

{
    "messagesConnection": apifu.TimeBasedConnection(&apifu.TimeBasedConnectionConfig{
        NamePrefix: "ChannelMessages",
        EdgeCursor: func(edge interface{}) apifu.TimeBasedCursor {
            message := edge.(*model.Message)
            return apifu.NewTimeBasedCursor(message.Time, string(message.Id))
        },
        EdgeFields: map[string]*graphql.FieldDefinition{
            "node": &graphql.FieldDefinition{
                Type: graphql.NewNonNullType(messageType),
                Resolve: func(ctx *graphql.FieldContext) (interface{}, error) {
                    return ctx.Object, nil
                },
            },
        },
        EdgeGetter: func(ctx *graphql.FieldContext, minTime time.Time, maxTime time.Time, limit int) (interface{}, error) {
            return ctxSession(ctx.Context).GetMessagesByChannelIdAndTimeRange(ctx.Object.(*model.Channel).Id, minTime, maxTime, limit)
        },
    }),
}

🛠 Can generate Apollo-like client-side type definitions and validate queries in source code.

The 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.

See cmd/gql-client-gen for details.

🚔 Calculates operation costs during validation for rate limiting and metering

During validation, you can specify a max operation cost or get the actual cost of an operation using customizable cost definitions:

doc, errs := graphql.ParseAndValidate(req.Query, req.Schema, req.ValidateCost(maxCost, &actualCost))

API Design Guidelines

The 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:

Versioning and Compatibility Guarantees

This 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.