• Stars
    star
    52
  • Rank 560,474 (Top 12 %)
  • Language
    Go
  • License
    MIT License
  • Created over 5 years ago
  • Updated 11 months ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

A collection of Go packages for creating robust GraphQL APIs

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

api-fu (noun)

  1. (informal) Mastery of APIs. πŸ’ͺ

Packages

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

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.

  • The jsonapi package is a library for building JSON:API 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).

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:

  • 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.
  • Nodes with 1-to-many relationships should make related nodes available via Relay Cursor Connections. 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.
  • 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.).
  • 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.
  • 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.

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.

More Repositories

1

wasm-go-playground

Go compiler running entirely in your browser
JavaScript
249
star
2

teslacam-recovery

Python script to help recover lost video from TeslaCam USB drives
Python
107
star
3

needy

A C++ library dependency helper.
Python
53
star
4

neural-net

An educational neural net library in Rust
Rust
40
star
5

poe-go

A library and guide for creating PoE applications in Go
Go
28
star
6

gggtracker

Grinding Gear activity tracker for Path of Exile.
Go
24
star
7

go-immutable

A collection of fast, general-purpose immutable data structures for Go.
Go
18
star
8

xiv-sim

Simulator for Final Fantasy 14.
C++
11
star
9

razer-chroma-rs

Razer Chroma SDK for Rust
Rust
10
star
10

factorystatsd

This is a Factorio mod for next level reliability engineering. Build dashboards for your factory in Datadog or get woken up by PagerDuty when incidents occur!
Lua
10
star
11

serverless-bot

Demo for Mattermost dev talk on serverless bot building.
Python
5
star
12

poe-tree-json-generator

Generates the JSON for the POE skill tree using game files.
Python
5
star
13

mdx-viewer

Some code for viewing MDX models from Blizzard's MPQ files. Likely very out-of-date.
C++
5
star
14

aarch64-std

implementations of standard library components for bare-metal aarch64
Rust
4
star
15

codex

A light-weight programmer's text editor.
Objective-C
3
star
16

compiler

Hand-written compiler for a c++-like language.
C++
2
star
17

xiv-lua

Lua add-on platform for Final Fantasy 14.
C++
2
star
18

axi-lite-sv

SystemVerilog
2
star
19

lessdb

Rust
2
star
20

dotfiles

.dotfiles
Shell
1
star
21

imagenerics

An imaginary library for an imaginary Go feature
1
star
22

axi-lite-vhdl

axi4-lite implementation in vhdl
Verilog
1
star
23

gba-emu

An emulator for the Game Boy Advance.
C++
1
star
24

xiv-sim-web

Web front-end for xiv-sim.
JavaScript
1
star
25

mfado

Like sudo, but for AWS+MFA.
Python
1
star
26

gostd

Rewriting Go packages in C++ from scratch (-nostdlib)
C++
1
star