{"id":37180220,"url":"https://github.com/alehechka/go-jsonapi","last_synced_at":"2026-01-14T20:57:19.214Z","repository":{"id":40316340,"uuid":"486004993","full_name":"alehechka/go-jsonapi","owner":"alehechka","description":"Go Module for creating JSON:API http servers","archived":false,"fork":false,"pushed_at":"2022-05-15T15:23:29.000Z","size":41,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"main","last_synced_at":"2024-06-21T03:04:51.649Z","etag":null,"topics":["json-api"],"latest_commit_sha":null,"homepage":"https://jsonapi.org","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/alehechka.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2022-04-27T01:31:58.000Z","updated_at":"2022-05-19T03:05:10.000Z","dependencies_parsed_at":"2022-07-22T08:53:01.175Z","dependency_job_id":null,"html_url":"https://github.com/alehechka/go-jsonapi","commit_stats":null,"previous_names":[],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/alehechka/go-jsonapi","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/alehechka%2Fgo-jsonapi","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/alehechka%2Fgo-jsonapi/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/alehechka%2Fgo-jsonapi/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/alehechka%2Fgo-jsonapi/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/alehechka","download_url":"https://codeload.github.com/alehechka/go-jsonapi/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/alehechka%2Fgo-jsonapi/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28434500,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-14T18:57:19.464Z","status":"ssl_error","status_checked_at":"2026-01-14T18:52:48.501Z","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":["json-api"],"created_at":"2026-01-14T20:57:18.567Z","updated_at":"2026-01-14T20:57:19.207Z","avatar_url":"https://github.com/alehechka.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# go-jsonapi\n\nThis Go module provides a useful API to create [JSON:API][jsonapi] HTTP servers. The primary usage of this library is to facilitate transformation from flattened Go structs into the standardized JSON:API [resource object][jsonapi-resource-object].\n\nAdditionally, there are optional methods that can be implemented with structs to add further standardized JSON:API structures such as links, relationships, included data, and metadata.\n\n## Installation\n\n```bash\ngo get github.com/alehechka/go-jsonapi\n```\n\nImport as:\n\n```go\nimport \"github.com/alehechka/go-jsonapi/jsonapi\"\n```\n\n## Usage\n\n### Defining a JSON:API struct\n\nThe primary resource object in JSON:API is of the following type:\n\n```json\n{\n\t\"data\": {\n\t\t\"id\": \"1234\",\n\t\t\"type\": \"people\",\n\t\t\"attributes\": {\n\t\t\t\"firstName\": \"John\",\n\t\t\t\"lastName\": \"Doe\",\n\t\t\t\"age\": 30\n\t\t}\n\t}\n}\n```\n\n- The `attributes` object will be generated from the struct itself.\n- The `id` field will be populated by the `ID()` interface method.\n- The `type` field will be populated by the `Type()` interface method.\n\n```go\ntype Person struct {\n    // It is recommended to omit the primary ID from json marshalling, but not required\n    PersonID    string  `json:\"-\"`\n    FirstName   string  `json:\"firstName\"`\n    LastName    string  `json:\"lastName\"`\n    Age         int     `json:\"age\"`\n}\n\nfunc (person Person) ID() string {\n    return person.PersonID\n}\n\nfunc (person Person) Type() string {\n    return \"people\"\n}\n```\n\n### Prepare for JSON marshalling\n\nTo prepare the struct for json marshalling it is required to use the provided `TransformResponse` or `TransformCollectionResponse` functions:\n\n```go\nresponse := jsonapi.TransformResponse(jsonapi.Response{\n    Node: Person{},\n    \"http://example.com\",\n})\n\nresponse := jsonapi.TransformCollectionResponse(jsonapi.CollectionResponse{\n    Nodes: []Person{},\n    \"http://example.com\",\n})\n```\n\nThe second parameter to these functions is for `baseURL`, this is used to dynamically populate relative URLs in `links` objects. More on this [here](#links).\n\n### Recommended Usage\n\nThe above functions are effectively the top-level transformation tools, however, the dynamic link creation can be made easy by supplying an `*http.Request` object to the following functions instead:\n\n```go\nreq := httptest.NewRequest(\"GET\", \"http://example.com/example\", nil)\n\nresponse := jsonapi.CreateResponse(req)(jsonapi.Response{\n    Node: Person{},\n})\n\nresponse := jsonapi.CreateCollectionResponse(req)(jsonapi.CollectionResponse{\n    Nodes: []Person{},\n})\n```\n\nThese versions will automatically extract the baseURL from the request and supply it to the respective `Transform` functions outlined above. This allows all generated links to display the same scheme and hostname as the server domain that the request was originally made to.\n\nAdditionally, using the `Create` functions will automatically generate a `self` link at the top-level object for every response.\n\n### Extending the top-level resource\n\nThe JSON:API spec also allows for `links`, `errors`, and `meta` objects at the top-level of the document. Both `jsonapi.Response` and `jsonapi.CollectionResponse` have values available for these.\n\n#### Links\n\nA top-level `links` object can be provided to both `Response` and `CollectionResponse`. See [Link](#link) below for further details.\n\n```go\nres := jsonapi.Response{\n    Links: jsonapi.Links{\n        jsonapi.NextKey: jsonapi.Link{\n            Href: \"/path/to/next/resource\",\n        },\n    },\n}\n```\n\n\u003e When using either `CreateResponse` or `CreateCollectionResponse` the `self` link will be automatically generated and always override an existing `self` link.\n\n#### Meta\n\nA top-level `meta` object can be provided to both `Response` and `CollectionResponse` in the form of any interface or key-value map.\n\n```go\nres := jsonapi.Response{\n    Meta: jsonapi.Meta{\n        \"page\": jsonapi.Meta{\n            \"size\": 10,\n            \"number\": 2,\n        },\n    },\n}\n```\n\n\u003e The `Meta` struct is simply an alias for `map[string]interface{}`\n\n#### Errors\n\nA top-level `errors` array can be provided to both `Response` and `CollectionResponse` in the form of an array of `Error` objects. See [Error](#error) below for further detail.\n\n```go\nres := jsonapi.Response{\n    Errors: jsonapi.Errors{\n        {\n            Status: http.StatusBadRequest,\n            Title: \"Error Occurred\",\n            Detail: \"Failed to retrieve resource\",\n        },\n    },\n}\n```\n\n\u003e It is important to note that if at least 1 error is present in this array than the top-level `data` object/array and `included` array will not be available as per the JSON:API spec for [Top Level][jsonapi-top-level].\n\n### Extending `Node` interface\n\nBy default, to be considered a JSON:API resource, a struct must include the `ID()` and `Type()` methods.\n\nHowever, this functionality can be extended further with other methods as follows:\n\n#### `Links()`\n\nThe `Links()` method allows an individual resource to generate the `links` object for itself using data from the object. See [Link](#link) below for further details.\n\n```go\nfunc (person Person) Links() jsonapi.Links {\n    return jsonapi.Links{\n        jsonapi.SelfKey: jsonapi.Link{\n            Href: \"/people/:id\",\n            Params: jsonapi.Params{\n                \"id\": person.ID(),\n            }\n        },\n    }\n}\n```\n\nThe above scenario makes use of the `Params` field which will not be included in the resulting json, but will use the key-value pairs to substitute the values into the `href` based on keys that it finds. (Ex. `:id` in the href will be substituted with the value of `person.ID()`)\n\n#### `Relationships()`\n\n[Relationships][jsonapi-relationships] are a key object within a resource to provide linkage and information about related resources. To facilitate the mapping, the `Relationships()` method gives access to the parent struct and allows definition of the `relationships` map as follows:\n\n```go\ntype Company struct {\n    CompanyID string `json:\"-\"`\n    Name string `json:\"name\"`\n    Address string `json:\"address\"`\n    Employees []Person `json:\"-\"` // recommended to omit children resources\n    Owner Person `json:\"-\"`\n}\n\nfunc (company Company) Relationships() map[string]interface{\n    return map[string]interface{}{\n        \"employees\": company.Employees,\n        \"owner\": company.Owner,\n    }\n}\n```\n\n\u003e In the above example it is crucial that the children relationship objects adhere to the JSON:API methods, i.e. initialize their own `ID()` and `Type()` methods.\n\n#### `RelationshipLinks(parentID string)`\n\nTypically in the `relationships` object, there will be included `links` object with links to the [related resources][jsonapi-related-links]. This can be facilitated by included the `RelationshipLinks(parentID string`) on children structs. The `parentID` parameter will automatically be supplied when generated as part of a relationship by the parent struct, it is recommended to use this in generating path params for the href variable.\n\n```go\nfunc (person Person) RelationshipLinks(companyID string) jsonapi.Links {\n    return jsonapi.Links{\n        jsonapi.SelfKey: jsonapi.Link{\n            Href: \"/companies/:companyID/relationships/employees\",\n            Params: jsonapi.Params{\n                \"companyID\": companyID,\n            },\n        },\n        jsonapi.RelatedKey: jsonapi.Link{\n            Href: \"/companies/:companyID/employees\",\n            Params: jsonapi.Params{\n                \"companyID\": companyID,\n            },\n        },\n    }\n}\n```\n\nIf the relationship will point to an array of resources, it is recommended to instead create a unique type for that array of structs as follows:\n\n```go\ntype People []Person\n\nfunc (people People) RelationshipLinks(companyID string) jsonapi.Links {\n    return jsonapi.Links{\n        jsonapi.SelfKey: jsonapi.Link{\n            Href: \"/companies/:companyID/relationships/employees\",\n            Params: jsonapi.Params{\n                \"companyID\": companyID,\n            },\n        },\n    }\n}\n```\n\n#### `Meta()`\n\nThe `Meta()` method is simply a means to generate a `meta` object for an individual resource by using the object as an input.\n\n```go\nfunc (person Person) Meta() interface{} {\n    return jsonapi.Meta{\n        \"fullName\": fmt.Sprintf(\"%s %s\", person.FirstName, person.LastName),\n    }\n}\n```\n\n### Structs Explained\n\n#### `Link`\n\nThe JSON:API [Links][jsonapi-document-links] states that each value of the `links` map must either be a string containing the link's URL or an object with an `href` and `meta` object. By, default, a `Link` object will be transformed into the string format in all cases expect when a non-nil, non-empty `Meta` object is provided.\n\n```go\nlinks := jsonapi.Links{\n    \"self\": jsonapi.Link{\n        Href: \"/path/to/resource\",\n    },\n    \"next\": jsonapi.Link{\n        Href: \"/path/to/next/resource\",\n        Meta: jsonapi.Meta{\n            \"page\": 3,\n        },\n    },\n}\n```\n\nAfter transformation and JSON marshalling assuming the provided `baseURL` is `http://example.com`, the result will be as follows:\n\n```json\n{\n\t\"links\": {\n\t\t\"self\": \"http://example.com/path/to/resource\",\n\t\t\"next\": {\n\t\t\t\"href\": \"http://example.com/path/to/next/resource\",\n\t\t\t\"meta\": {\n\t\t\t\t\"page\": 3\n\t\t\t}\n\t\t}\n\t}\n}\n```\n\nAdditionally, the `Link` object provides options for `Params` and `Queries`. These will always be ignored in the JSON marshalling and are used to help generate the `href` URL.\n\n- `Params` is a map of key-value pairs that represent path parameters. During transformation, href path sections that are prefixed with a colon (`:`), will be substituted with the value of a matching key in the `Params` map.\n- `Queries` is a map of key-value pairs that represent query parameters. During transformation, all key-value pairs will be generated and appended to the href as query parameters. Pre-existing query parameters in the supplied href will not be removed, but will be replaced if they have the same key.\n\n```go\nlinks := jsonapi.Links{\n    \"self\": jsonapi.Link{\n        Href: \"/path/to/resource/:id?page[size]=20\"\n        Params: jsonapi.Params{\n            \"id\": 1234,\n        },\n        Queries: jsonapi.Queries{\n            \"page[number]\": 4,\n        },\n    },\n}\n```\n\nAfter transformation and JSON marshalling assuming the provided `baseURL` is `http://example.com`, the result will be as follows:\n\n```json\n{\n\t\"links\": {\n\t\t\"self\": \"http://example.com/path/to/resource/1234?page[size]=20\u0026page[limit]=4\"\n\t}\n}\n```\n\n\u003e For further details, view the implementation here: [/jsonapi/links.go](/jsonapi/links.go#L35-L44)\n\n#### `Error`\n\nThe JSON:API `Errors` specification includes a large number of fields, all of which can be supplied to the provided `Error` object. The internal `links` object of `Error` will also be supplied the `baseURL` and follow the same transformation rules outlined [above](#link).\n\n```go\nerrs := jsonapi.Errors{\n    {\n        Status: http.StatusBadRequest,\n        Title: \"Standard Error Occurred\",\n        Detail: \"Further Detail is supplied here\",\n    },\n}\n```\n\nAfter transformation and JSON marshalling, the result will be as follows:\n\n```json\n{\n\t\"errors\": [\n\t\t{\n\t\t\t\"status\": 400,\n\t\t\t\"title\": \"Standard Error Occurred\",\n\t\t\t\"detail\": \"Further Detail is supplied here\"\n\t\t}\n\t]\n}\n```\n\n\u003e For further details, view the implementation here: [/jsonapi/errors.go](/jsonapi/errors.go#L10-L19)\n\n\u003c!--- Links --\u003e\n\n[jsonapi]: (https://jsonapi.org/)\n[jsonapi-resource-object]: (https://jsonapi.org/format/#document-resource-objects)\n[jsonapi-top-level]: (https://jsonapi.org/format/#document-top-level)\n[jsonapi-relationships]: (https://jsonapi.org/format/#document-resource-object-relationships)\n[jsonapi-related-links]: (https://jsonapi.org/format/#document-resource-object-related-resource-links)\n[jsonapi-document-links]: (https://jsonapi.org/format/#document-links)\n[jsonapi-errors]: (https://jsonapi.org/format/#errors)\n[gin]: (https://github.com/gin-gonic/gin)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Falehechka%2Fgo-jsonapi","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Falehechka%2Fgo-jsonapi","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Falehechka%2Fgo-jsonapi/lists"}