{"id":37100304,"url":"https://github.com/maxbeatty/api2go","last_synced_at":"2026-01-14T12:13:21.157Z","repository":{"id":30362410,"uuid":"33914923","full_name":"maxbeatty/api2go","owner":"maxbeatty","description":"JSONAPI.org Implementation for Go","archived":false,"fork":true,"pushed_at":"2015-04-14T06:55:02.000Z","size":311,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"master","last_synced_at":"2024-06-21T03:15:38.845Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":false,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":"manyminds/api2go","license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/maxbeatty.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}},"created_at":"2015-04-14T06:33:41.000Z","updated_at":"2016-04-10T18:28:00.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/maxbeatty/api2go","commit_stats":null,"previous_names":[],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/maxbeatty/api2go","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/maxbeatty%2Fapi2go","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/maxbeatty%2Fapi2go/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/maxbeatty%2Fapi2go/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/maxbeatty%2Fapi2go/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/maxbeatty","download_url":"https://codeload.github.com/maxbeatty/api2go/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/maxbeatty%2Fapi2go/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28419699,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-14T10:47:48.104Z","status":"ssl_error","status_checked_at":"2026-01-14T10:46:19.031Z","response_time":107,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: 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":"2026-01-14T12:13:20.520Z","updated_at":"2026-01-14T12:13:21.149Z","avatar_url":"https://github.com/maxbeatty.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# api2go\n\n[![GoDoc](https://godoc.org/github.com/univedo/api2go?status.svg)](https://godoc.org/github.com/univedo/api2go)\n[![Build Status](https://travis-ci.org/univedo/api2go.svg?branch=master)](https://travis-ci.org/univedo/api2go)\n\nA [JSON API](http://jsonapi.org) Implementation for Go, to be used e.g. as server for [Ember Data](https://github.com/emberjs/data).\n\n```go\nimport \"github.com/univedo/api2go\"\n```\n\n**api2go works, but we're still working on some rough edges. Things might change. Open an issue and join in!**\n\n** we are currently re-implementing a lot of stuff in a cleaner way with interfaces and upgrading to the RC3 (Final) Standard of jsonapi.org**\n\nNote: if you only need the marshaling functionality, you can install the subpackage via\n ```go\ngo get github.com/univedo/api2go/jsonapi\n```\n\n## Examples\n\nExamples can be found [here](https://github.com/univedo/api2go/blob/master/examples/crud_example.go).\n\n## Usage\n\nTake the simple structs:\n\n```go\ntype Post struct {\n  ID          int\n  Title       string\n  Comments    []Comment `json:\"-\"` // this will be ignored by the api2go marshaller\n  CommentsIDs []int     `json:\"-\"` // it's only useful for our internal relationship handling\n}\n\ntype Comment struct {\n  ID   int\n  Text string\n}\n```\n\n### Interfaces to implement\nYou must at least implement one interface for api2go to work, which is the one for marshalling/unmarshalling the primary `ID` of the struct\nthat 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,\na UUID or a BSON Object for MongoDB etc...\n\nIf the struct already has a field named `ID`, or `Id`, it will be ignored automatically. If your ID field has a different name, please use the\njson ignore tag.\n\n#### MarshalIdentifier\n```go\ntype MarshalIdentifier interface {\n\tGetID() string\n}\n```\n\nImplement this interface to marshal a struct.\n\n#### UnmarshalIdentifier\n```go\ntype UnmarshalIdentifier interface {\n\tSetID(string) error\n}\n```\n\nThis is the corresponding interface to MarshalIdentifier. Implement this interface in order to unmarshal incoming json into\na struct.\n\n#### Marshalling with References to other structs\nFor relationships to work, there are 3 Interfaces that you can use:\n\n```go\ntype MarshalReferences interface {\n\tGetReferences() []Reference\n}\n\n// MarshalLinkedRelations must be implemented if there are references and the reference IDs should be included\ntype MarshalLinkedRelations interface {\n\tMarshalReferences\n\tMarshalIdentifier\n\tGetReferencedIDs() []ReferenceID\n}\n\n// MarshalIncludedRelations must be implemented if referenced structs should be included\ntype MarshalIncludedRelations interface {\n\tMarshalReferences\n\tMarshalIdentifier\n\tGetReferencedStructs() []MarshalIdentifier\n}\n```\n\nHere, you can choose what you want to implement too, but, you must at least implement `MarshalReferences` and `MarshalLinkedRelations`.\n\n`MarshalReferences` must be implemented in order for api2go to know which relations are possible for your struct.\n\n`MarshalLinkedRelations` must be implemented to retrieve the `IDs` of the relations that are connected to this struct. This method\ncould also return an empty array, if there are currently no relations. This is why there is the `MarshalReferences` interface, so that api2go\nknows what is possible, even if nothing is referenced at the time.\n\nIn addition to that, you can implement `MarshalIncludedRelations` which exports the complete referenced structs and embeds them in the json\nresult inside the `included` object.\n\nWe choose to do this because it gives you better flexibility and eliminates the conventions in the previous versions of api2go. **You can\nnow choose how you internally manage relations.** So, there are no limits regarding the use of ORMs.\n\n#### Unmarshalling with references to other structs\nIncoming jsons can also contain reference IDs. In order to unmarshal them correctly, you have to implement the following interface\n\n```go\ntype UnmarshalLinkedRelations interface {\n\tSetReferencedIDs([]ReferenceID) error\n}\n```\n\n**If you need to know more about how to use the interfaces, look at our tests or at the example project.**\n\n### Ignoring fields\napi2go ignores all fields that are marked with the `json\"-\"` ignore tag. This is useful if your struct has some more\nfields which are only used internally to manage relations or data that needs to stay private, like a password field.\n\n### Manual marshaling / unmarshaling\n\n```go\ncomment1 = Comment{ID: 1, Text: \"First!\"}\ncomment2 = Comment{ID: 2, Text: \"Second!\"}\npost = Post{ID: 1, Title: \"Foobar\", Comments: []Comment{comment1, comment2}}\n\njson, err := api2go.MarshalJSON(post)\n```\n\nwill yield\n\n```json\n{\n  \"data\": [\n    {\n      \"id\": \"1\",\n      \"type\": \"posts\",\n      \"links\": {\n        \"comments\": {\n          \"linkage\": [\n            {\n              \"id\": \"1\",\n              \"type\": \"comments\"\n            },\n            {\n              \"id\": \"2\",\n              \"type\": \"comments\"\n            }\n          ],\n          \"resource\": \"\\/posts\\/1\\/comments\"\n        }\n      },\n      \"title\": \"Foobar\"\n    }\n  ],\n  \"included\": [\n    {\n      \"id\": \"1\",\n      \"type\": \"comments\",\n      \"text\": \"First!\"\n    },\n    {\n      \"id\": \"2\",\n      \"type\": \"comments\",\n      \"text\": \"Second!\"\n    }\n  ]\n}\n```\n\nRecover the structure from above using\n\n```go\nvar posts []Post\nerr := api2go.UnmarshalFromJSON(json, \u0026posts)\n// posts[0] == Post{ID: 1, Title: \"Foobar\", CommentsIDs: []int{1, 2}}\n```\n\nNote that when unmarshaling, api2go will always fill the `CommentsIDs` field, never the `Comments` field.\n\n### Building a REST API\n\nFirst, write an implementation of `api2go.DataSource`. You have to implement 5 methods:\n\n```go\ntype fixtureSource struct {}\n\nfunc (s *fixtureSource) FindAll(r api2go.Request) (interface{}, error) {\n  // Return a slice of all posts as []Post\n}\n\nfunc (s *fixtureSource) FindOne(ID string, r api2go.Request) (interface{}, error) {\n  // Return a single post by ID as Post\n}\n\nfunc (s *fixtureSource) FindMultiple(IDs []string, r api2go.Request) (interface{}, error) {\n  // Return multiple posts by ID as []Post\n  // For example for Requests like GET /posts/1,2,3\n}\n\nfunc (s *fixtureSource) Create(obj interface{}, r api2go.Request) (string, error) {\n  // Save the new Post in `obj` and return its ID.\n}\n\nfunc (s *fixtureSource) Delete(id string, r api2go.Request) error {\n  // Delete a post\n}\n\nfunc (s *fixtureSource) Update(obj interface{}, r api2go.Request) error {\n  // Apply the new values in the Post in `obj`\n}\n```\n\nAs an example, check out the implementation of `fixtureSource` in [api_test.go](/api_test.go).\n\nYou can then create an API:\n\n```go\napi := api2go.NewAPI(\"v1\")\napi.AddResource(Post{}, \u0026PostsSource{})\nhttp.ListenAndServe(\":8080\", api.Handler())\n```\n\nThis generates the standard endpoints:\n\n```\nOPTIONS /v1/posts\nOPTIONS /v1/posts/\u003cid\u003e\nGET     /v1/posts\nPOST    /v1/posts\nGET     /v1/posts/\u003cid\u003e\nPUT     /v1/posts/\u003cid\u003e\nDELETE  /v1/posts/\u003cid\u003e\nGET     /v1/posts/\u003cid\u003e,\u003cid\u003e,...\nGET     /v1/posts/\u003cid\u003e/comments\n```\n\n#### Query Params\nTo support all the features mentioned in the `Fetching Resources` section of Jsonapi:\nhttp://jsonapi.org/format/#fetching\n\nIf you want to support any parameters mentioned there, you can access them in your Resource\nvia the `api2go.Request` Parameter. This currently supports `QueryParams` which holds\nall query parameters as `map[string][]string` unfiltered. So you can use it for:\n  * Filtering\n  * Inclusion of Linked Resources\n  * Sparse Fieldsets\n  * Sorting\n  * Aything else you want to do that is not in the official Jsonapi Spec\n\n```go\ntype fixtureSource struct {}\n\nfunc (s *fixtureSource) FindAll(req api2go.Request) (interface{}, error) {\n  for key, values range req.QueryParams {\n    ...\n  }\n  ...\n}\n```\n\nIf there are multiple values, you have to separate them with a comma. api2go automatically\nslices the values for you.\n\n```\nExample Request\nGET /people?fields=id,name,age\n\nreq.QueryParams[\"fields\"] contains values: [\"id\", \"name\", \"age\"]\n```\n\n### Using Pagination\nApi2go can automatically generate the required links for pagination. Currently there are 2 combinations of query\nparameters supported:\n\n- page[number], page[size]\n- page[offset], page[limit]\n\nPagination is optional. If you want to support pagination, you have to implement the `PaginatedFindAll` method\nin you resource struct. For an example, you best look into our example project.\n\nExample request\n\n```\nGET /v0/users?page[number]=2\u0026page[size]=2\n```\n\nwould return a json with the top level links object\n\n```json\n{\n  \"links\": {\n    \"first\": \"http://localhost:31415/v0/users?page[number]=1\u0026page[size]=2\",\n    \"last\": \"http://localhost:31415/v0/users?page[number]=5\u0026page[size]=2\",\n    \"next\": \"http://localhost:31415/v0/users?page[number]=3\u0026page[size]=2\",\n    \"prev\": \"http://localhost:31415/v0/users?page[number]=1\u0026page[size]=2\"\n  },\n  \"data\": [...]\n}\n```\n\n### Loading related resources\nApi2go always creates a `resource` property for elements in the `links` property of the result. This is like it's\nspecified on jsonapi.org. Post example:\n\n```json\n{\n  \"data\": [\n    {\n      \"id\": \"1\",\n      \"type\": \"posts\",\n      \"title\": \"Foobar\",\n      \"links\": {\n        \"comments\": {\n          \"resource\": \"\\/v1\\/posts\\/1\\/comments\",\n          \"linkage\": [\n            {\n              \"id\": \"1\",\n              \"type\": \"comments\"\n            },\n            {\n              \"id\": \"2\",\n              \"type\": \"comments\"\n            }\n          ]\n        }\n      }\n    }\n  ]\n}\n```\n\nIf a client requests this `resource` url, the `FindAll` method of the comments resource will be called with a query\nparameter `postsID`.\n\nSo if you implement the `FindAll` method, do not forget to check for all possible query Parameters. This means you have\nto check all your other structs and if it references the one for that you are implementing `FindAll`, check for the\nquery Paramter and only return comments that belong to it. In this example, return the comments for the Post.\n\n\n## Tests\n\n```sh\ngo test\nginkgo                # Alternative\nginkgo watch -notify  # Watch for changes\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmaxbeatty%2Fapi2go","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmaxbeatty%2Fapi2go","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmaxbeatty%2Fapi2go/lists"}