{"id":37958937,"url":"https://github.com/mfcochauxlaberge/jsonapi","last_synced_at":"2026-01-16T18:02:26.632Z","repository":{"id":43331712,"uuid":"82139898","full_name":"mfcochauxlaberge/jsonapi","owner":"mfcochauxlaberge","description":"Set of tools to build a JSON:API compliant service.","archived":false,"fork":false,"pushed_at":"2022-10-10T17:46:49.000Z","size":1190,"stargazers_count":22,"open_issues_count":21,"forks_count":8,"subscribers_count":4,"default_branch":"master","last_synced_at":"2025-08-14T17:42:23.057Z","etag":null,"topics":["go","json-api"],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"gpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/mfcochauxlaberge.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":"2017-02-16T04:41:16.000Z","updated_at":"2023-11-08T21:55:06.000Z","dependencies_parsed_at":"2022-07-12T18:18:56.493Z","dependency_job_id":null,"html_url":"https://github.com/mfcochauxlaberge/jsonapi","commit_stats":null,"previous_names":["kkaribu/jsonapi"],"tags_count":24,"template":false,"template_full_name":null,"purl":"pkg:github/mfcochauxlaberge/jsonapi","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mfcochauxlaberge%2Fjsonapi","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mfcochauxlaberge%2Fjsonapi/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mfcochauxlaberge%2Fjsonapi/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mfcochauxlaberge%2Fjsonapi/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/mfcochauxlaberge","download_url":"https://codeload.github.com/mfcochauxlaberge/jsonapi/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mfcochauxlaberge%2Fjsonapi/sbom","scorecard":{"id":562568,"data":{"date":"2025-08-11","repo":{"name":"github.com/mfcochauxlaberge/jsonapi","commit":"c61299292bf747e333f2c06a48bfeec66ef07c08"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":3.7,"checks":[{"name":"Packaging","score":-1,"reason":"packaging workflow not detected","details":["Warn: no GitHub/GitLab publishing workflow detected."],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#packaging"}},{"name":"Code-Review","score":0,"reason":"Found 0/30 approved changesets -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#code-review"}},{"name":"Maintained","score":0,"reason":"0 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#maintained"}},{"name":"Dangerous-Workflow","score":10,"reason":"no dangerous workflow patterns detected","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#dangerous-workflow"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#cii-best-practices"}},{"name":"Token-Permissions","score":0,"reason":"detected GitHub workflow tokens with excessive permissions","details":["Warn: no topLevel permission defined: .github/workflows/lint.yml:1","Warn: no topLevel permission defined: .github/workflows/test.yml:1","Info: no jobLevel write permissions found"],"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#token-permissions"}},{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#binary-artifacts"}},{"name":"Pinned-Dependencies","score":0,"reason":"dependency not pinned by hash detected -- score normalized to 0","details":["Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/lint.yml:13: update your workflow using https://app.stepsecurity.io/secureworkflow/mfcochauxlaberge/jsonapi/lint.yml/master?enable=pin","Warn: third-party GitHubAction not pinned by hash: .github/workflows/lint.yml:15: update your workflow using https://app.stepsecurity.io/secureworkflow/mfcochauxlaberge/jsonapi/lint.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/test.yml:16: update your workflow using https://app.stepsecurity.io/secureworkflow/mfcochauxlaberge/jsonapi/test.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/test.yml:19: update your workflow using https://app.stepsecurity.io/secureworkflow/mfcochauxlaberge/jsonapi/test.yml/master?enable=pin","Warn: third-party GitHubAction not pinned by hash: .github/workflows/test.yml:21: update your workflow using https://app.stepsecurity.io/secureworkflow/mfcochauxlaberge/jsonapi/test.yml/master?enable=pin","Info:   0 out of   3 GitHub-owned GitHubAction dependencies pinned","Info:   0 out of   2 third-party GitHubAction dependencies pinned"],"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#pinned-dependencies"}},{"name":"Security-Policy","score":0,"reason":"security policy file not detected","details":["Warn: no security policy file detected","Warn: no security file to analyze","Warn: no security file to analyze","Warn: no security file to analyze"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#security-policy"}},{"name":"License","score":10,"reason":"license file detected","details":["Info: project has a license file: LICENSE:0","Info: FSF or OSI recognized license: GNU General Public License v3.0: LICENSE:0"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#fuzzing"}},{"name":"Signed-Releases","score":-1,"reason":"no releases found","details":null,"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#signed-releases"}},{"name":"Branch-Protection","score":-1,"reason":"internal error: error during branchesHandler.setup: internal error: githubv4.Query: Resource not accessible by integration","details":null,"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#branch-protection"}},{"name":"SAST","score":0,"reason":"SAST tool is not run on all commits -- score normalized to 0","details":["Warn: 0 commits out of 14 are checked with a SAST tool"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}},{"name":"Vulnerabilities","score":9,"reason":"1 existing vulnerabilities detected","details":["Warn: Project is vulnerable to: GO-2022-0603 / GHSA-hp87-p4gw-j4gq"],"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}}]},"last_synced_at":"2025-08-20T14:07:29.282Z","repository_id":43331712,"created_at":"2025-08-20T14:07:29.282Z","updated_at":"2025-08-20T14:07:29.282Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28480513,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-16T11:59:17.896Z","status":"ssl_error","status_checked_at":"2026-01-16T11:55:55.838Z","response_time":107,"last_error":"SSL_read: 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":["go","json-api"],"created_at":"2026-01-16T18:02:25.185Z","updated_at":"2026-01-16T18:02:26.624Z","avatar_url":"https://github.com/mfcochauxlaberge.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cdiv align=\"center\" style=\"text-align: center;\"\u003e\n  \u003cimg src=\"https://raw.githubusercontent.com/mfcochauxlaberge/jsonapi/master/assets/logo.png\" height=\"120\"\u003e\n  \u003cbr\u003e\n  \u003ca href=\"https://github.com/mfcochauxlaberge/jsonapi/actions?query=workflow%3ATest+branch%3Amaster\"\u003e\n    \u003cimg src=\"https://github.com/mfcochauxlaberge/jsonapi/workflows/Test/badge.svg?branch=master\"\u003e\n  \u003c/a\u003e\n  \u003ca href=\"https://github.com/mfcochauxlaberge/jsonapi/actions?query=workflow%3ALint+branch%3Amaster\"\u003e\n    \u003cimg src=\"https://github.com/mfcochauxlaberge/jsonapi/workflows/Lint/badge.svg?branch=master\"\u003e\n  \u003c/a\u003e\n  \u003ca href=\"https://goreportcard.com/report/github.com/mfcochauxlaberge/jsonapi\"\u003e\n    \u003cimg src=\"https://goreportcard.com/badge/github.com/mfcochauxlaberge/jsonapi\"\u003e\n  \u003c/a\u003e\n  \u003ca href=\"https://codecov.io/gh/mfcochauxlaberge/jsonapi\"\u003e\n    \u003cimg src=\"https://img.shields.io/codecov/c/github/mfcochauxlaberge/jsonapi\"\u003e\n  \u003c/a\u003e\n  \u003cbr\u003e\n  \u003ca href=\"https://github.com/mfcochauxlaberge/jsonapi/blob/master/go.mod\"\u003e\n    \u003cimg src=\"https://img.shields.io/badge/go%20version-1.13%2B-%2300acd7\"\u003e\n  \u003c/a\u003e\n  \u003ca href=\"https://github.com/mfcochauxlaberge/jsonapi/blob/master/go.mod\"\u003e\n    \u003cimg src=\"https://img.shields.io/github/v/release/mfcochauxlaberge/jsonapi?include_prereleases\u0026sort=semver\"\u003e\n  \u003c/a\u003e\n  \u003ca href=\"https://github.com/mfcochauxlaberge/jsonapi/blob/master/LICENSE\"\u003e\n    \u003cimg src=\"https://img.shields.io/github/license/mfcochauxlaberge/jsonapi?color=a33\"\u003e\n  \u003c/a\u003e\n  \u003ca href=\"https://pkg.go.dev/github.com/mfcochauxlaberge/jsonapi?tab=doc\"\u003e\n    \u003cimg src=\"https://img.shields.io/static/v1?label=doc\u0026message=pkg.go.dev\u0026color=007d9c\"\u003e\n  \u003c/a\u003e\n\u003c/div\u003e\n\n# jsonapi\n\njsonapi offers a set of tools to build JSON:API compliant services.\n\nThe official JSON:API specification can be found at [jsonapi.org/format](http://jsonapi.org/format).\n\n## Features\n\njsonapi offers the following features:\n\n* Marshaling and unmarshaling of JSON:API URLs and documents\n* Structs for handling URLs, documents, resources, collections...\n* Schema management\n  * It can ensure relationships between types make sense.\n  * Very useful for validation when marshaling and unmarshaling.\n* Utilities for pagination, sorting, and filtering\n  * jsonapi is opiniated when it comes to those features. If you prefer you own strategy fo pagination, sorting, and filtering, it will have to be done manually.\n* In-memory data store (`SoftCollection`)\n  * It can store resources (anything that implements `Resource`).\n  * It can sort, filter, retrieve pages, etc.\n  * Enough to build a demo API or use in test suites.\n  * Not made for production use.\n* Other useful helpers\n\n## State\n\nThe library is in **beta** and its API is subject to change until v1 is released.\n\nIn terms of features, jsonapi is complete. The work left is polishing and testing the design of current API.\n\n### Roadmap to v1\n\nWhile anything can happen before a v1 release, the API is stable and no big changes are expected at this moment.\n\nA few tasks are required before committing to the current API:\n\n* Rethink how errors are handled\n  * Use the new tools introduced in Go 1.13.\n* Simplify the API\n  * Remove anything that is redundant or not useful.\n* Gather feedback from users\n  * The library should be used more on real projects to see of the API is convenient.\n\n## Requirements\n\nThe supported versions of Go are the latest patch releases of every minor release starting with Go 1.13.\n\n## Examples\n\nThe best way to learn and appreciate this package is to look at the simple examples provided in the `examples/` directory.\n\n## Quick start\n\nThe simplest way to start using jsonapi is to use the MarshalDocument and UnmarshalDocument functions.\n\n```go\nfunc MarshalDocument(doc *Document, url *URL) ([]byte, error)\nfunc UnmarshalDocument(payload []byte, schema *Schema) (*Document, error)\n```\n\nA 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.\n\nSee the following section for more information about how to define structs for this library.\n\n## Concepts\n\nHere are some of the main concepts covered by the library.\n\n### Request\n\nA `Request` represents an HTTP request structured in a format easily readable from a JSON:API point of view.\n\nIf you are familiar with the specification, reading the `Request` struct and its fields (`URL`, `Document`, etc) should be straightforward.\n\n### Schema\n\nA `Schema` contains all the schema information for an API, like types, fields, relationships between types, and so on. See `schema.go` and `type.go` for more details.\n\nThis is really useful for many uses cases:\n\n* Making sure the schema is coherent\n* Validating resources\n* Parsing documents and URLs\n* And probably many more...\n\nFor example, when a request comes in, a `Document` and a `URL` can be created by parsing the request. By providing a schema, the parsing can fail if it finds some errors like a type that does not exist, a field of the wrong kind, etc. After that step, valid data can be assumed.\n\n### Type\n\nA JSON:API type is generally defined with a struct.\n\nThere needs to be an ID field of type string. The `api` tag represents the name of the type.\n\n```go\ntype User struct {\n  ID string `json:\"id\" api:\"users\"` // ID is mandatory and the api tag sets the type\n\n  // Attributes\n  Name string `json:\"name\" api:\"attr\"` // attr means it is an attribute\n  BornAt time.Time `json:\"born-at\" api:\"attr\"`\n\n  // Relationships\n  Articles []string `json:\"articles\" api:\"rel,articles\"`\n}\n```\n\nOther fields with the `api` tag (`attr` or `rel`) can be added as attributes or relationships.\n\n#### Attribute\n\nAttributes can be of the following types:\n\n```go\nstring\nint, int8, int16, int32, int64\nuint, uint8, uint16, uint32, uint64\nbool\ntime.Time\n[]byte\n*string\n*int, *int8, *int16, *int32, *int64\n*uint, *uint8, *uint16, *uint32, *uint64\n*bool\n*time.Time\n*[]byte\n```\n\nUsing a pointer allows the field to be nil.\n\n#### Relationship\n\nRelationships 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:\n\n```go\nAuthor string `json:\"author\" api:\"rel,users,articles\"`\n```\n\n### Wrapper\n\nA 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.\n\n```go\nuser := User{}\nwrap := Wrap(\u0026user)\nwrap.Set(\"name\", \"Mike\")\nfmt.Printf(wrap.Get(\"name\")) // Output: Mike\nfmt.Printf(user.Name) // Output: Mike\n```\n\n### SoftResource\n\nA 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.\n\n```go\nsr := SoftResource{}\nsr.AddAttr(Attr{\n  Name:     \"attr\",\n  Type:     AttrTypeInt,\n  Nullable: false,\n})\nfmt.Println(sr.Get(\"attr\")) // Output: 0\n```\n\nTake a look at the `SoftCollection` struct for a similar concept applied to an entire collection of resources.\n\n### URLs\n\nFrom 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.\n\nIt is also possible to build a `URL` from a `Schema` and a `SimpleURL` which contains additional information taken from the schema. `NewURL` returns an error if the URL does not respect the schema.\n\n## Documentation\n\nCheck out the [documentation](https://pkg.go.dev/github.com/mfcochauxlaberge/jsonapi?tab=doc).\n\nThe best way to learn how to use it is to look at documentation, the examples, and the code itself.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmfcochauxlaberge%2Fjsonapi","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmfcochauxlaberge%2Fjsonapi","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmfcochauxlaberge%2Fjsonapi/lists"}