-
-
Notifications
You must be signed in to change notification settings - Fork 150
(JSON)API examples
This page serves as a discussion board for the various API definitions we're considering and the pros and cons of each. The first discussion point is the GL transaction resource.
A GL transaction API, when modelled as a REST resource might look like (without HATEOAS):
{
"id": 9,
"date": "...",
"description": "...",
"notes": "...",
"lines": [
{ "account": { "accno": "...", "description": "...", "id": 12 },
"amount": 23.2, "curr": "EUR", },
{ "account": { "accno": "...", "description": "...", "id": 11 },
"amount": -23.2, "curr": "EUR", },
],
"created": {
"date": "...",
"by": "...",
},
"approved": { }
The same resource could look like:
{
"data": {
"id": 9,
"type": "gl-transaction",
"attributes": {
"date": "...",
"description": "...",
"notes": "...",
"lines": [
{ "account": { "accno": "...", "description": "...", "id": 12 },
"amount": 23.2, "curr": "EUR", },
{ "account": { "accno": "...", "description": "...", "id": 11 },
"amount": -23.2, "curr": "EUR", },
],
}
},
"meta": {
"created": {
"date": "...",
"by": "...",
},
"approved": { }
}
}
Note that this design similarly doesn't have links to other resources (HATEOAS). There are a few other things to note about it:
- JSONAPI only defines how to change 'toplevel'
attributes
andrelationships
, meaning thatlines
will need to be edited as a whole; - the
account
object in each line contains unmodifiable data, except for theid
- since the
account
object isn't a toplevel field, it's not an option to tranform it into arelationship
, meaning it's not possible to link the to an external resource
Applying the same reasoning that no external resource links are required for invoices, the resource might look like the ones below
{
"id": 9,
"customer": {
"id": 1200,
"name": "ABC",
},
"date": "...",
"description": "...",
"notes": "...",
"lines": [
{ "invoice": { "part": { "id": 99, "onhand": 999, "price": 5.00 } },
"gl": { "account": { "accno": "...", "description": "...",
"id": 12 }, "amount": 23.2, "curr": "EUR", } },
{ "gl": { "account": { "accno": "...", "description": "...",
"id": 11 }, "amount": -23.2, "curr": "EUR", } },
],
"created": {
"date": "...",
"by": "...",
},
"approved": { }
{
"data": {
"type": "invoice",
"id": 9,
"relationships": {
"customer": { "data": { "id": 1200, "type": "customer" } }
},
"attributes": {
"date": "...",
"description": "...",
"notes": "...",
"lines": [
{ "invoice": { "part": { "id": 99, "onhand": 999, "price": 5.00 } },
"gl": { "account": { "accno": "...", "description": "...",
"id": 12 }, "amount": 23.2, "curr": "EUR", } },
{ "gl" : { "account": { "accno": "...", "description": "...",
"id": 11 }, "amount": -23.2, "curr": "EUR", } },
],
},
"meta": {
"created": {
"date": "...",
"by": "...",
},
"approved": { }
}
}
The above definitions are off though: the onhand
field shouldn't be part of the invoice. The field is part of the informational data provided to the user during the invoice-entry process, but the value is really associated with the part itself.
A more natural design would therefore be that the part be relationship data of sorts, which would allow the JSONAPI server to return the related part data (and thus on hand
data) with the request, instead of "in" the invoice data. Complicating matter to do this, is that the JSONAPI doesn't define a way to reference resources other than on top-level fields; yet the 'part' data in the invoice isn't a top-level field.
The problem described above - the fact that the parts data isn't top-level data - can be solved by making each invoice line a separate sub-resource of the invoice, linking to those from the invoice, and linking to the parts from those.
This solution feels wrong: an invoice is the collection of its lines. The collection of lines is the defining property of the invoice. To that extent, it seems wrong to define the lines as separate sub-resouces. (To that extent, the lines of an invoice are very much something different to an invoice than addresses to a customer -- the latter being something that I can see a sub-resource.)
Below there is a "solution" to this problem: the server generates a read-only surrogate field invoice-parts
which does contain the relationships with the parts used in the invoice. The existence of this relationship will allow the server to include parts data (and therefore onhand
numbers).
{
"data": {
"type": "invoice",
"id": 9,
"relationships": {
"customer": { "data": { "id": 1200, "type": "customer" } },
"invoice-parts": [
{ "data": { "id": 99, "type": "part" } },
],
},
"attributes": {
"date": "...",
"description": "...",
"notes": "...",
"lines": [
{ "invoice": { "part": { "id": 99, "price": 5.00 } },
"gl": { "account": { "accno": "...", "description": "...",
"id": 12 }, "amount": 23.2, "curr": "EUR", } },
{ "gl" : { "account": { "accno": "...", "description": "...",
"id": 11 }, "amount": -23.2, "curr": "EUR", } },
],
},
"meta": {
"created": {
"date": "...",
"by": "...",
},
"approved": { }
},
"included": [
{ "data": { "type": "part", "id": 99, "onhand": 999 } },
]
}