Api2go
JSONAPI.org Implementation for Go
Install / Use
/learn @manyminds/Api2goREADME
api2go
A JSON API Implementation for Go, to be used e.g. as server for Ember Data.
TOC
- Installation
- Basic functionality
- Examples
- Interfaces to implement
- Manual marshalling / unmarshalling
- SQL Null-Types
- Using api2go with the gin framework
- Building a REST API
- Tests
Installation
For the complete api2go package use:
go get github.com/manyminds/api2go
If you only need marshalling and/or unmarshalling:
go get github.com/manyminds/api2go/jsonapi
Basic functionality
Api2go will Marshal/Unmarshal exactly like the internal json package from Go
with one addition: It will decorate the Marshalled json with jsonapi meta
objects. Jsonapi wraps the payload inside an attributes object. The rest is
just Meta-Data which will be generated by api2go.
So let's take this basic example:
type Article struct {
ID string
Title string `json:"title"`
}
Would json.Marshal into this Json:
{
"ID": "Some-ID",
"title": "the title"
}
For api2go, you have to ignore tag the ID field and then the result could be
something like this:
{
"type": "articles",
"id": "1",
"attributes": {
"title": "Rails is Omakase"
},
"relationships": {
"author": {
"links": {
"self": "/articles/1/relationships/author",
"related": "/articles/1/author"
},
"data": { "type": "people", "id": "9" }
}
}
}
All the additional information is retrieved by implementing some interfaces.
Examples
- Basic Examples can be found here.
- For a more real life example implementation of api2go using jinzhu/gorm and gin-gonic/gin you can have a look at hnakamur's repository
Interfaces to implement
For the following query and result examples, imagine the following 2 structs which represent a posts and comments that belong with a has-many relation to the post.
type Post struct {
ID int `json:"-"` // Ignore ID field because the ID is fetched via the
// GetID() method and must not be inside the attributes object.
Title string `json:"title"`
Comments []Comment `json:"-"` // this will be ignored by the api2go marshaller
CommentsIDs []int `json:"-"` // it's only useful for our internal relationship handling
}
type Comment struct {
ID int `json:"-"`
Text string `json:"text"`
}
You must at least implement the MarshalIdentifier interface, which is the one for marshalling/unmarshalling the primary ID of the struct
that you want to marshal/unmarshal. This is because of the huge variety of types that you could use for the primary ID. For example a string,
a UUID or a BSON Object for MongoDB etc...
In the Post example struct, the ID field is ignored because api2go will use the GetID method that you implemented
for your struct to fetch the ID of the struct.
Every field inside a struct will be marshalled into the attributes object in
the json. In our example, we just want to have the Title field there.
Don't forget to name all your fields with the json:"yourName" tag.
Responder
type Responder interface {
Metadata() map[string]interface{}
Result() interface{}
StatusCode() int
}
The Responder interface must be implemented if you are using our API. It contains everything that is needed for a response. You can see an example usage of it in our example project.
EntityNamer
type EntityNamer interface {
GetName() string
}
EntityNamer is an optional interface. Normally, the name of
a struct will be automatically generated in its plural form. For example if
your struct has the type Post, its generated name is posts. And the url
for the GET request for post with ID 1 would be /posts/1.
If you implement the GetName() method and it returns special-posts, then
this would be the name in the type field of the generated json and also the
name for the generated routes.
Currently, you must implement this interface, if you have a struct type that
consists of multiple words and you want to use a hyphenized name. For example UnicornPost.
Our default Jsonifier would then generate the name unicornPosts. But if you
want the recommended name, you
have to implement GetName
func (s UnicornPost) GetName() string {
return "unicorn-posts"
}
MarshalIdentifier
type MarshalIdentifier interface {
GetID() string
}
Implement this interface to marshal a struct.
UnmarshalIdentifier
type UnmarshalIdentifier interface {
SetID(string) error
}
This is the corresponding interface to MarshalIdentifier. Implement this interface in order to unmarshal incoming json into a struct.
Marshalling with References to other structs
For relationships to work, there are 3 Interfaces that you can use:
type MarshalReferences interface {
GetReferences() []Reference
}
// MarshalLinkedRelations must be implemented if there are references and the reference IDs should be included
type MarshalLinkedRelations interface {
MarshalReferences
MarshalIdentifier
GetReferencedIDs() []ReferenceID
}
// MarshalIncludedRelations must be implemented if referenced structs should be included
type MarshalIncludedRelations interface {
MarshalReferences
MarshalIdentifier
GetReferencedStructs() []MarshalIdentifier
}
Implementing those interfaces is not mandatory and depends on your use cases. If your API has any relationships,
you must at least implement MarshalReferences and MarshalLinkedRelations.
MarshalReferences must be implemented in order for api2go to know which relations are possible for your struct.
MarshalLinkedRelations must be implemented to retrieve the IDs of the relations that are connected to this struct. This method
could also return an empty array, if there are currently no relations. This is why there is the MarshalReferences interface, so that api2go
knows what is possible, even if nothing is referenced at the time.
In addition to that, you can implement MarshalIncludedRelations which exports the complete referenced structs and embeds them in the json
result inside the included object.
That way you can choose how you internally manage relations. So, there are no limits regarding the use of ORMs.
Unmarshalling with references to other structs
Incoming jsons can also contain reference IDs. In order to unmarshal them correctly, you have to implement the following interfaces. If you only have to-one
relationships, the UnmarshalToOneRelations interface is enough.
// UnmarshalToOneRelations must be implemented to unmarshal to-one relations
type UnmarshalToOneRelations interface {
SetToOneReferenceID(name, ID string) error
}
// UnmarshalToManyRelations must be implemented to unmarshal to-many relations
type UnmarshalToManyRelations interface {
SetToManyReferenceIDs(name string, IDs []string) error
}
If you need to know more about how to use the interfaces, look at our tests or at the example project.
Manual marshalling / unmarshalling
Please keep in mind that this only works if you implemented the previously mentioned interfaces. Manual marshalling and
unmarshalling makes sense, if you do not want to use our API that automatically generates all the necessary routes for you. You
can directly use our sub-package github.com/manyminds/api2go/jsonapi
comment1 = Comment{ID: 1, Text: "First!"}
comment2 = Comment{ID: 2, Text: "Second!"}
post = Post{ID: 1, Title: "Foobar", Comments: []Comment{comment1, comment2}}
json, err := jsonapi.Marshal(post)
will yield
{
"data": [
{
"id": "1",
"type": "posts",
"attributes": {
"title": "Foobar"
},
"relationships": {
"comments": {
"data": [
{
"id": "1",
"type": "comments"
},
{
"id": "2",
"type": "comments"
}
]
}
}
}
],
"included": [
{
"id": "1",
"type": "comments",
"attributes"
Related Skills
bluebubbles
337.1kUse when you need to send or manage iMessages via BlueBubbles (recommended iMessage integration). Calls go through the generic message tool with channel="bluebubbles".
gh-issues
337.1kFetch GitHub issues, spawn sub-agents to implement fixes and open PRs, then monitor and address PR review comments. Usage: /gh-issues [owner/repo] [--label bug] [--limit 5] [--milestone v1.0] [--assignee @me] [--fork user/repo] [--watch] [--interval 5] [--reviews-only] [--cron] [--dry-run] [--model glm-5] [--notify-channel -1002381931352]
healthcheck
337.1kHost security hardening and risk-tolerance configuration for OpenClaw deployments
node-connect
337.1kDiagnose OpenClaw node connection and pairing failures for Android, iOS, and macOS companion apps
