Enough about data, let's  talk action.
Tags: architecture, rest, api
Published: 2020-10-12 13:07:31

You will label me old school, but I really like my frontend and my backend to be strongly separated. I've resisted the urge to go full javascript for just that reason. Because if you use python or PHP on the backend, laziness will win and you won't reimplement your objects and models in js/ts in the frontend. There, time can be spent doing something else.

Doing what you may ask ? Thinking about about the experience of our users. I've found that, as developpers, we rely on other people to do that for us (designer, ux, marketing), because we certainly don't want to talk to them. So in order to not have to talk to them, we make excuse and reimplement the data model in the front. Because that will be useful, right ?

It won't.

First, for the most basic reason : there's so much data in the backend that you never want leaked to the frontend. And even that is not that simple. There's private-private data, and private data a user has the right to see, that some other has the right to edit etc.
On the frontend, data presentation is dependant on _context_. So just translating what you have on the backend to the frontend is an illusory quest.

Second : when trying to reimplement the model in the frontend, you will want to reimplement the relations. And store them. You will invoke graphql and localstorage as your tools of choice. They're nice indeed. But they won't help you accomplish anything if you don't know what your user want to _do_.

Those two words are at the heart of what's important to me :
- What a user can _do_
- What is the user _context_

Because the user don't really care about the model. More often than not, the user's mental model won't even be 1-1 with the software model. Because the human mind is way more flexible that any [reference database type](https://en.wikipedia.org/wiki/Slowly_changing_dimension) and your user is probably more preoccupied by its own business (selling socks, cutting wood or managing contracts) than by what you think of it. 

Your user want shortcuts. 
Your user wants to use the CPU of your server, a lot. They want to transform data in a meaningful way. Because in the end, the data that you store with a pretty model on your server is useless to them if they can't be used in the real world. Your user works with socks, logs and broadcast reports, they already have the actual real data in their warehouse.

What your user doesn't want is to translate what they are doing  into a database relation with many-to-one parameters or whatever. They don't care.
They don't want to mark a pack of socks as `"status": SHIPPED`, they want to _ship it_. Don't talk to them about the `status` property of the `ShipmentRow` of the `Shipment` object. That's meaningless to them.
And if it's meaningless to the user in front of it's screen, it's probably useless to include it in the frontend code.

Let's have a simple example.
Let's say our user, a seller of socks, has an order (2 Red & Yellow and 1 Red & Blue) on it's website.
The end-user client calls on the phone the seller and say "Finally, sorry, but I don't want the Green & Blue".

In a typical app, you would do the following :
```
GET /shipments
200 =>
[
 {"id": 1234,
  "title": "Shipment of socks",
  "status": "PENDING_SHIPMENT",
  "rows: [
    {"id": "1234-1", "quantity": 2, "product_name": "Red & Yellow"},
    {"id": "1234-2", "quantity": 1, "product_name": "Green & Blue"}
  ]
 },
 {/* another shipment */}
]

DELETE /shipment/1234/row/2
205 =>
"deleted"

PUT /shipment/1234
{"status": "SHIPPED"}
205 =>
"updated"
```

That's 3 http requests. A click to get the list, one to delete the extra row (maybe 1 more to confirm the deletion and 1 extra if the UI is "checkbock + submit") and, one to change the status in a select, and one to submit the form : total of 5±1.
Moreover, you have to implement ACL logic in 3 backend actions to check if the connected user has the right to see, delete a row and ship it. And you have to implement the same logic in the frontend so that you display the relevant buttons.

Now, let's do that with _user's actions and context_ on our mind.
We'll begin by the first `GET`.

```
GET /shipments
200 =>
{
 "shipments":[
   {"id": 1234,
    "title": "Shipment of socks",
    "rows": [
      {"id": "1234-1", "quantity": 2, "product_name": "Red & Yellow", "user_can": ["change_row_quantity"]},
      {"id": "1234-2", "quantity": 1, "product_name": "Green & Blue", "user_can": ["change_row_quantity"]}
    ],
    "user_can": ["ship", "cancel"]
   }
 ],
 "actions":{
    "ship":{"method":"POST", "href":"/shipment/:id/ship", 
            "params":{
                "mode":{
                  "type": "enum", 
                  "values": "FULL|PARTIAL"
                }
            }
           },
    "cancel": {"method":"DELETE", "href":"/shipment/:id"},
    "change_row_quantity": {"method": "PUT", "href":"/shipment_row/:id/quantity", "params": {"quantity": {"type": "posint"}}},
    "destination_change": {"method":"PUT", 
                           "href":"/shipment/:id", 
                           "params": {"address":{"type": "Address"}}
                          }
  }
}
```
There, you have everything, and you didn't even know that you could change the destination of the shipment. Well, your user can't, the backend said it. No need to implement the logic in the frontend. We can even declare sub actions on the `ShipmentRows` if the interface allows it. The frontend just has to read the response and do its job (displaying stuff) accordingly.

Now, let's partial ship the order 1234 ! No need to configure it in the frontend, the href and params are documented right there.

```
POST /shipment/1234/ship
{"mode": "PARTIAL"}
400 =>
{"error": "missing_parameter", 
 "missing_parameter": {
     "kept_rows": {
        "type": "int[]",
        "suggested_values":[
          {"key": "1234-1", "label": "Red & Yellow"},
          {"key": "1234-2", "label": "Green & Blue"}
        ]
     }
}
```

What happened there ? We tried to ship it without telling it the rows we wanted to keep ! We could have added that to the listed parameters, but as we try to create shortcut for the user, we shouldn't anticipate too much and burden the frontend with things it hasn't have to deal at the moment.
It also demonstrates that you can use backend error codes to create a flow.
And that you should be helpful to the frontend. Here, we could just have said `missing_parameter: kept_rows` and rely on external documentation to implement the behaviour in the frontend. But we decided to walk the extra mile and return documentation **and** suggested data so that the frontend doesn't even have to think ! Aren't we nice ?

In this case, I usually display a popin with the suggested missing parameters. And send the exact same request, enriched. That popin also contains a warning message that the rows that are not chosen will be lost (I never say "deleted" because people think that you can "undelete" things.).

```
POST /shipment/1234/ship
{"mode": "PARTIAL", "kept_rows": ["1234-1"]}
200 =>
{
 "shipment":
   {"id": 1234,
    "title": "Shipment of socks",
    "shipped_at": "2020-10-05 10:00:00",
    "rows": [
      {"id": "1234-1", "quantity": 2, "product_name": "Red & Yellow"},
    ],
    "user_can": ["return"]
   }
 ,
 "actions":{
    "return": {"method":"POST", "href":"/shipment/:id/return"}
  }
}
```
Finally, the shipment is sent with updated informations. The `shipped_at` property didn't make any sense before, so it was omitted, we don't want the frontend to deal with `null` or meaningless values. Now that it's useful, we give it back.
The available actions are updated and this should be reflected by the frontend.

Here, again, 3 http calls on only 2 endpoints. But only one click to display the list, one to click "partial shipment", one to select the row to keep and one to submit the form = 4 click total. That's 20% less friction to get things done.
That's maintenance saved on the frontend because you implement the logic only once.
That's work saved everywhere because you do not implement things that can't be acted upon.
User context is fully integrated in the backend and transmitted to the front whose job, I think, is to be the exoskeleton of the user, amplifying their power and actions.

---
Credit header photo thanks to [Mark Chan](https://unsplash.com/@markcjn?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText) on Unsplash.