Hatchify implements JSON:API 1.1. The JSON:API specification leaves some decisions up to the implementor, so this document reviews Hatchify's implementation specifically:
- Reading Lists
- Reading a single record
- Creating a record
- Updating a record
- Deleting a record
- Unsupported features
The following is an overview of HatchifyJS's JSON:API implementation:
To get a list of records, you do something like:
GET /api/sales-people
and the response will look like
{
"jsonapi": {
"version": "1.0"
},
"data": [
{
"type": "SalesPerson",
"id": "ffaf131e-9e27-4bd6-a715-59fff9ed5044",
"attributes": {
"name": "Justin",
"dateHired": "2000-01-01"
}
},
{
"type": "SalesPerson",
"id": "e38bff50-61dd-40ce-b29c-725cb9a2735f",
"attributes": {
"name": "Roye",
"dateHired": "2020-01-01"
}
}
],
"meta": {
"unpaginatedCount": 2
}
}
However thanks to JSON:API the query string is able to filter, paginate, sort, include relationships and sparse fields.
While the JSON:API specs suggests having a filter
query string parameter, they are agnostic about the strategies supported by the server. HatchifyJS supports "MongoDB" style filtering operators below:
GET /api/sales-people? filter[name]=Justin filter[name]=%00 // %00 is an encoded null value filter[name][$eq]=Justin filter[name][$ne]=Justin filter[name][$in]=Justin&filter[name][$in]=Roye filter[name][$nin]=Justin&filter[name][$nin]=Roye filter[name][$gt]=Roye filter[name][$gte]=Arthur filter[name][$gte]=Arthur filter[name][$lt]=Roye filter[name][$lte]=Roye filter[name][$ilike]=startsWith%25 // %25 is a % encoded filter[name][$ilike]=%25endsWith filter[name][$ilike]=%25contains%25 filter[name][$like]=startsWithCaseSensitive%25
A response of filtered results will look similar to the one below where irrelevant results will be omitted based on the query string parameters:
{
"jsonapi": {
"version": "1.0"
},
"data": [
{
"type": "SalesPerson",
"id": "ffaf131e-9e27-4bd6-a715-59fff9ed5044",
"attributes": {
"name": "Justin",
"dateHired": "2000-01-01"
}
}
],
"meta": {
"unpaginatedCount": 1
}
}
Read more on HatchifyJS's filtering support
The JSON:API specs allows a page
query string parameter to control pagination but they are agnostic about the strategies supported by the server. HatchifyJS supports a few pagination strategies you can choose from:
GET /api/sales-people? page[limit]=5 page[offset]=10 page[number]=2 page[size]=20
A response of paginated results will look similar to the one below and when the unpaginatedCount
value is greater than the amount of results it indicates there is more data available than the requested page size:
{
"jsonapi": {
"version": "1.0"
},
"data": [
{
"type": "SalesPerson",
"id": "ffaf131e-9e27-4bd6-a715-59fff9ed5044",
"attributes": {
"name": "Justin",
"dateHired": "2000-01-01"
}
}
],
"meta": {
"unpaginatedCount": 10
}
}
Read more on HatchifyJS's paginating support
One of the JSON:API builtin advantages is to pull in compacted related references:
GET /api/sales-people? include=todos,accounts,accounts.owner
A response of results with related data will look similar to the one below:
{
"jsonapi": {
"version": "1.0"
},
"data": [
{
"type": "SalesPerson",
"id": "ffaf131e-9e27-4bd6-a715-59fff9ed5044",
"attributes": {
"name": "Justin",
"dateHired": "2000-01-01"
},
"relationships": {
"todos": {
"data": [
{
"type": "Todo",
"id": "d65c0ab4-b61e-4147-97f3-cba1a0a88e03"
}
]
},
"accounts": {
"data": [
{
"type": "Account",
"id": "77667b72-534c-4c22-9b01-7b49158d6015"
}
]
}
}
}
],
"included": [
{
"type": "Todo",
"id": "d65c0ab4-b61e-4147-97f3-cba1a0a88e03",
"attributes": {
"name": "Cleanup"
}
},
{
"type": "Account",
"id": "77667b72-534c-4c22-9b01-7b49158d6015",
"attributes": {
"name": "Acme"
},
"relationships": {
"owner": {
"data": {
"type": "User",
"id": "77667b72-534c-4c22-9b01-7b49158d6015"
}
}
}
},
{
"type": "User",
"id": "77667b72-534c-4c22-9b01-7b49158d6015",
"attributes": {
"name": "Justin"
}
}
],
"meta": {
"unpaginatedCount": 1
}
}
Read more on HatchifyJS's relationship support
The JSON:API specs have detailed guidelines how to implement the sort
query string parameter. HatchifyJS implements these with added support for sorting relationships as well:
GET /api/sales-people? include=salesGroup sort=-importance,salesGroup.name
Read more on HatchifyJS's sorting support
The JSON:API specs have detailed guidelines how to implement the fields
query string parameter. HatchifyJS implements it with a slight difference in casing of the schemas:
GET /api/sales-people? include=todos &fields[SalesPerson]=name &fields[Todo]=name,importance
A response with sparse fields will look similar to we we had above but only specified fields will be returned:
{
"jsonapi": {
"version": "1.0"
},
"data": [
{
"type": "SalesPerson",
"id": "ffaf131e-9e27-4bd6-a715-59fff9ed5044",
"attributes": {
"name": "Justin"
},
"relationships": {
"todos": {
"data": [
{
"type": "Todo",
"id": "d65c0ab4-b61e-4147-97f3-cba1a0a88e03"
}
]
}
}
}
],
"included": [
{
"type": "Todo",
"id": "d65c0ab4-b61e-4147-97f3-cba1a0a88e03",
"attributes": {
"name": "Cleanup",
"importance": 7
}
}
],
"meta": {
"unpaginatedCount": 1
}
}
Read more on HatchifyJS's sparse fields support
When you know the ID of the record you are looking for, you can fetch it like:
GET /api/sales-people/ffaf131e-9e27-4bd6-a715-59fff9ed5044
And get a successful response that looks like:
{
"jsonapi": {
"version": "1.0"
},
"data": {
"type": "SalesPerson",
"id": "ffaf131e-9e27-4bd6-a715-59fff9ed5044",
"attributes": {
"name": "Justin",
"dateHired": "2000-01-01"
}
}
}
Relationships and sparse fields from above are also supported when reading a single record.
The JSON:API specs allows creation of a new resource and attaching it to pre-existing IDs in the same request:
POST /api/sales-people
Content-Type: application/vnd.api+json
{
"jsonapi": {
"version": "1.0"
},
"data": {
"type": "SalesPerson",
"attributes": {
"name": "Justin",
"dateHired": "2000-01-01"
},
"relationships": {
"todos": {
"data": [
{
"type": "Todo",
"id": "d65c0ab4-b61e-4147-97f3-cba1a0a88e03"
}
]
}
}
}
}
Successful response includes an ID and no relationships even though we are linking them:
{
"jsonapi": {
"version": "1.0"
},
"data": {
"type": "SalesPerson",
"id": "ffaf131e-9e27-4bd6-a715-59fff9ed5044",
"attributes": {
"name": "Justin",
"dateHired": "2000-01-01"
}
}
}
Read more on HatchifyJS's creating
The JSON:API specs allows update of an existing resource and attaching it to pre-existing IDs in the same request:
PATCH /api/sales-people/ffaf131e-9e27-4bd6-a715-59fff9ed5044
Content-Type: application/vnd.api+json
{
"jsonapi": {
"version": "1.0"
},
"data": {
"type": "SalesPerson",
"id": "ffaf131e-9e27-4bd6-a715-59fff9ed5044",
"attributes": {
"dateHired": "2020-01-01"
},
"relationships": {
"todos": {
"data": [
{
"type": "Todo",
"id": "0f6fc95d-622e-43cc-9a44-0b7ef7793be3"
}
]
}
}
}
}
Successful response includes an ID and no relationships. Behind the scenes, all linked todos will be unlinked from the sales person and the new todo will be linked now. If other relationships exist they will not be touched.
{
"jsonapi": {
"version": "1.0"
},
"data": {
"type": "SalesPerson",
"id": "ffaf131e-9e27-4bd6-a715-59fff9ed5044",
"attributes": {
"dateHired": "2020-01-01"
}
}
}
Read more on HatchifyJS's updating
The JSON:API specs allows deletion of an existing resource by its ID:
DELETE /api/sales-people/ffaf131e-9e27-4bd6-a715-59fff9ed5044
Successful response will look like:
{
"jsonapi": {
"version": "1.0"
},
"data": null
}
Read more on HatchifyJS's deleting
/relationships
endpoints. Currently we have to update the record which will overwrite all existing relationships.