The db-view approach - or how to avoid pitfalls by designing your API exclusively for one frontend app

Hi,

my current venture is Storrito.com, where we had the chance to develop a system with Clojure, ClojureScript and Datomic from the ground up.

I guess everyone who builds his own business has serious time pressure. Therefore I care a lot about the amount of effort it takes to accomplish common development tasks. After a lot of attempts I ended up choosing different trade-offs than a REST API or GraphQL. I wrote about the reasoning here:

https://maxweber.github.io/blog/2019-06-15-approaching-the-web-after-tomorrow-part-3

I would be thankful for feedback about the solution I’ve chosen, I named it db-view and wrote about it here:

https://maxweber.github.io/blog/2019-07-25-introducing-db-view-part-1

A few days ago I also finished an example app that shows the db-view approach in action:

https://maxweber.github.io/blog/2019-08-05-introducing-db-view-part-2

Best regards and a great day,

Max

3 Likes

Hello, Max!

Welcome to ClojureVerse!

I just read all five posts, and really appreciated them. The Clojure community is full of good ideas, which sometimes leaves a need for practical guides like this. I might be using this approach myself. Thanks for taking the time!

Teodor

Hi Teodor,

thanks a lot for the welcome and taking the time to read all five posts. I’m very happy to hear that you might be using this approach. The next post will also cover the command part in more detail, but half of this post is already in the source code comments :slightly_smiling_face:

Max

1 Like

Thank you for sharing your thoughts.

I read your series of posts, and though you mention that you don’t think graphql is a good fit for your needs, your solution looks quite similar, at least on the surface. I am guessing there is something more to it that I didn’t catch from your posts. Can you perhaps elaborate on the differences some more?

Again, your thoughts are appreciated. I always enjoy reading about ideas on how to structure my code.

Thanks a lot for the feedback. db-view definitely have a few things in common with GraphQL. Most notably that the client tells the server, what parts it likes to receive and that all of them are returned in a single response. GraphQL is great, if you have a segregation between frontend and backend teams. It offers the frontend team a kind of self-service, since they now have a query language, similar (but less powerful) like a backend developer has SQL for the database. But providing an API with a query language like GraphQL is a significant effort.

If you are the developer who programs both, the frontend and the backend, you don’t need this kind of client-side query language, you can simply write your queries on the server-side. There are less options for an attacker to mess with your API. For example GraphQL APIs often uses a query hash to only allow predefined queries, since it is difficult to fully ensure that the API user does not traverse the graph to nodes which he is not allowed to read. Last but not least the whole GraphQL package weighs a lot, not only in terms of code / lib size, it also takes a while to learn all its details (its type system, the syntax of the query language etc.). The db-view approach doesn’t really require a dedicated lib, since you need so less code, every required part is included in the small example app: https://github.com/maxweber/todomvc-db-view

While GraphQL uses mutations to cause side-effects on the server, db-view has its command concept. Unique to db-view is that it even moves the creation of commands to the read part (or the “pure” / side-effect-free part, if you think of it from a functional programming perspective). If an user should not be allowed to delete a todo item, you just do not include the corresponding (encrypted) command in the response. Normally your “mutations” API endpoint intertwines the side-effect and the validation. Since it either performs the side-effect or response with a validation error. With db-view for example you provide the text for a new todo item in the db-view request and receive either a command or a validation error in the response. But performing the side-effect that is described by the command is a separate step.

2 Likes

Thanks for posting this.

We are about to start a new project using Clojure and Datomic, and I have been toying with the idea of not using GraphQL, specifically for our own front-end (typically the same developer will do both front- and backend). Like you mention here, GraphQL adds a bunch of extra work (and because resolvers are done on individual field level, to implement it efficiently can quickly add complexity, something which most GraphQL examples/articles conveniently skip).

I have at times been surprised when I heard other teams that use Datomic and Clojure, ClojureScript, be very, very enthusiastic about GraphQL. It has its place (especially if you want open up APIs to 3rd parties), but to me it feels like something you should be doing only when necessary.

I really look forward to reading your articles.

Hey there,

as promised here the last part of the db-view introduction that explains how to do side effects (commands) in a db-view application:

https://maxweber.github.io/blog/2019-08-20-introducing-db-view-part-3

I’m happy to hear your feedback.

Best regards and a great day,

Max

1 Like

To clarify, any side effects require two round trips to server? The first as a query to essentially ask “validate this command I’m about to send” and second, taking the response and actually sending that to command endpoint? I guess if you need to hit db-view endpoint for other information anyways, merging this first command-query into one of them can happen. If the client has already issued a command, why receive the actual encrypted command map then send it back? Why not validate, send result of validation to client for optimistic update purposes, but also directly process in the server?

This looks like a much more minimal, hand-rolled version of what Fulcro tries to do. Very helpful to see examples like this, thanks!

Thanks a lot for the feedback. Not every side effect requires two round trips to the server. The command to mark a todo-item as done for example, does not need any additional input. Therefore the db-view for the todo list already returns an encrypted command for each todo-item. Sure, you still have 2 requests, but the first one was to fetch the db-view normally.

If the user is not allowed to mark a todo-item as done, your server just omits the corresponding encrypted command entries and the UI can react to this by disabling or removing the corresponding button.

If the client has already issued a command, why receive the actual encrypted command map then send it back? Why not validate, send result of validation to client for optimistic update purposes, but also directly process in the server?

Using the command approach is not a requirement to use db-view. You can for sure also build other API endpoints which directly process the command if it is valid.

However I noticed during the development of the first prototype that the validation itself is a read-only / “pure” operation. But it is most often intertwined with an API endpoint that does side-effects. Therefore I moved the validation over to the read-only / “pure” part of the API.

Last week I also found a very interesting example of LiveView in Clojure:

If you like to dive deeper into these topics.

1 Like

This topic was automatically closed 182 days after the last reply. New replies are no longer allowed.