Skip to content
Library to marshal and unmarshal JSON:API payloads.
Go
Branch: master
Clone or download
mfcochauxlaberge Skip to-many check in Equal if both lengths are zero
reflect.DeepEqual returns false if a nil slice is compared to an
empty slice. For the purpose of the Equal function, both a nil and
an empty slice are considered equal which is not an opinion that
the reflect.DeepEqual function shares.
Latest commit d1e6300 Aug 20, 2019
Permalink
Type Name Latest commit message Commit time
Failed to load latest commit information.
testdata
.codecov.yml
.gitignore Add new line at the end of dotfiles Aug 18, 2019
.golangci.yml
.travis.yml
LICENSE.txt
README.md
build_links.go
build_links_test.go
collection.go Add Resources type Aug 17, 2019
collection_test.go
doc.go Fix code examples in doc.go Jul 1, 2019
document.go Add line length linter and fix related code Aug 4, 2019
document_test.go
error.go
error_test.go Make Error.Status a string Aug 18, 2019
filter_query.go Add line length linter and fix related code Aug 4, 2019
filter_query_test.go
go.mod
go.sum
helpers.go
helpers_test.go Rename Reflect to BuildType Aug 14, 2019
identifiers.go
identifiers_test.go
jsonapi.go Implement unmarshaling Aug 18, 2019
jsonapi_test.go
link.go
link_test.go
logo.png
marshaling_test.go Make Error.Status a string Aug 18, 2019
mock_schema_test.go
params.go
range.go
range_test.go
request.go
request_test.go
resource.go
resource_test.go
schema.go
schema_test.go
simple_url.go
simple_url_test.go
simple_url_util_test.go
skeletons.go
soft_collection.go
soft_collection_test.go
soft_resource.go
soft_resource_test.go
type.go Add top comment to Type.New and Type.Equal Aug 18, 2019
type_test.go
unmarshaling_test.go
url.go
url_test.go
util_test.go
wrapper.go Move interface checks to test files Aug 13, 2019
wrapper_collection.go
wrapper_collection_test.go
wrapper_test.go

README.md

jsonapi

jsonapi is a complete library to marshal and unmarshal JSON:API payloads.

It also offers many tools for parsing URLs, handling resources, managing schemas, and building other tools on top of this library.

The official specification can be found at jsonapi.org/format.

State

The library is in beta and its API is subject to change until v1 is released.

Quick start

The simplest way to start using jsonapi is to use the Marshal and Unmarshal functions.

func Marshal(doc *Document, url *URL) ([]byte, error)
func Unmarshal(payload []byte, url *URL, schema *Schema) (*Document, error)

A struct has to follow certain rules in order to be understood by the library, but interfaces are also provided which let the library avoid the reflect package and be more efficient.

See the following section for more information about how to define structs for this library.

Concepts

Here are some of the main concepts covered by the library.

Request

A Request represents an HTTP request structured in a format easily readable from a JSON:API point of view.

If you are familiar to the specification, reading the Request struct and its fields (URL, Document, etc) should be straightforward.

Type

A JSON:API type is generally defined with a struct.

There needs to be an ID field of type string. The api tag represents the name of the type.

type User struct {
  ID string `json:"id" api:"users"` // ID is mandatory and the api tag sets the type

  // Attributes
  Name string `json:"name" api:"attr"` // attr means it is an attribute
  BornAt time.Time `json:"born-at" api:"attr"`

  // Relationships
  Articles []string `json:"articles" api:"rel,articles"`
}

Other fields with the api tag (attr or rel) can be added as attributes or relationships.

Attribute

Attributes can be of the following types:

string
int, int8, int16, int32, int64
uint, uint8, uint16, uint32, uint64
bool
time.Time
*string
*int, *int8, *int16, *int32, *int64
*uint, *uint8, *uint16, *uint32, *uint64
*bool
*time.Time

Relationship

Relationships can be a bit tricky. To-one relationships are defined with a string and to-many relationships are defined with a slice of strings. They contain the IDs of the related resources. The api tag has to take the form of "rel,xxx[,yyy]" where yyy is optional. xxx is the type of the relationship and yyy is the name of the inverse relationship when dealing with a two-way relationship. In the following example, our Article struct defines a relationship named author of type users:

Author string `json:"author" api:"rel,users,articles"`

Wrapper

A struct can be wrapped using the Wrap function which returns a pointer to a Wrapper. A Wrapper implements the Resource interface and can be used with this library. Modifying a Wrapper will modify the underlying struct. The resource's type is defined from reflecting on the struct.

user := User{}
wrap := Wrap(&user)
wrap.Set("name", "Mike")
fmt.Printf(wrap.Get("name")) // Output: Mike
fmt.Printf(user.Name) // Output: Mike

SoftResource

A SoftResource is a struct whose type (name, attributes, and relationships) can be modified indefinitely just like its values. When an attribute or a relationship is added, the new value is the zero value of the field type. For example, if you add an attribute named my-attribute of type string, then softresource.Get("my-attribute") will return an empty string.

sr := SoftResource{}
sr.AddAttr(Attr{
  Name: "attr",
  Type: AttrTypeInt,
  Null: false,
})
fmt.Println(sr.Get("attr")) // Output: 0

Take a look at the SoftCollection struct for a similar concept applied to an entire collection of resources.

URLs

From a raw string that represents a URL, it is possible that create a SimpleURL which contains the information stored in the URL in a structure that is easier to handle.

It is also possible to build a URL from a Schema and a SimpleURL which contains additional information taken from the schema. NewURL returns a error if the URL does not respect the schema.

Documentation

Check out the incomplete documentation.

The best way to learn how to use it is to look at the source code and its comments.

You can’t perform that action at this time.