Click Bait?
I’ll admit I might be a bit guilty of clickbaiting in the title. As with most things in the consulting world, the answer to the question of which one is better is: “it depends”. However, with the introduction of the Web API, I have seen a move away from using Liquid for read operations, and I think often the advantages of using Liquid and FetchXML are not considered.
The answer to the question of why I think Liquid and FetchXML is often better comes down to one word: security.
Server-side vs Client-side
The main reason I like Liquid is that it is a server-side language. This means that the code itself is executed on the server, and the client (that is, the user’s browser), never gets to see the code, nor can they edit the code. All they see is the end result of what the code produces. Users have no ability to craft and execute their own queries using Liquid.
Contrast that with the Web API. While the Web API code itself runs server-side, we interact with it via JavaScript. If you enable the Web API for a particular Dataverse table, your options for controlling security include Table Permissions, and restricting which columns are exposed. But you don’t have any control on what type of queries someone could craft themselves and execute using the exposed Web API endpoint. You have to assume that a user can access any data they have permissions for. While this might seem obvious, I’ll discuss an example shortly where we don’t want this to be the case.
In other terms, when you enable the Web API, you open a door to Dataverse, and you don’t have nearly the same control over that door as you do when you use Liquid and FetchXML.
Now, to be clear, if you have permissions configured correctly, it can be quite appropriate to have that door open via the Web API. However, if you could instead open a much smaller door, why wouldn’t you?
Anonymous Access
A classic use case for Liquid and FetchXML instead of the Web API would be any queries needed for anonymous users.
Because of the way Table Permissions work, the only type of anonymous access you can provide is Global (all other access types are based on how rows are related to the currently logged in contact, which doesn’t exist for anonymous users). So if you enable the Web API for a table, and anonymous users have global table permissions to that table, they can query any row in the system. They’d only be limited based on the columns that have been enabled for the Web API.
Now, you may craft your Web API queries to only pull information that is relevant for a particular anonymous user. However, those queries are done in JavaScript, and anyone with basic web development skills would be able to modify that JavaScript and execute any query they’d like. Adding filters in your Web API queries so that the users only see what you want them to see is an example of security through obscurity, which is not security at all.
For example, let’s say you want anonymous users to be able to search a table based on a particular unique ID where you expect them to only know that unique ID for one row. You could write a beautiful JavaScript interface that allows them to search by ID, and make the query only return a result if the ID exactly matched. This would prevent them from seeing data where they don’t know the ID, right?
Wrong. It would be easy to modify the JavaScript code, because it is client-side, to remove the filtering based on ID. Then the query would return all the results.
Contrast this with using Liquid and FetchXML. You can build a page that accepts the ID as a query string parameter and performs the search. In your Liquid code, you could have logic that says if there is no ID provided as a query string parameter, don’t even perform the query. Or have logic that says if multiple rows are returned by the query, display nothing because that is an unexpected result. The key here is that this logic is written in Liquid, executed on the server, and cannot be modified by the user, no matter how smart they are.
If you prefer an architecture where you are using JavaScript to call an API, Liquid and FetchXML is still an option. You can create a “custom web service” by creating a web page that uses Web Templates, Liquid and FetchXML to return JSON that can be called from JavaScript. Within that custom page, you can implement all of your security logic server-side, so you’re still getting the security benefits.
Data Leaks and Vulnerabilities
Back in November 2024, my blog post covered a couple of data leaks where Power Pages sites were involved, the second of which involved the Web API. And since then, additional vulnerabilities with the Web API, which thankfully have been patched, were announced by the security company that found them.
The issues highlight the risks associated with the Web API. In the case of the data leaks, it was a situation where Power Pages makers open a door without understanding the ramifications. In the case of the vulnerabilities, it was a situation where the door ended up being bigger than documented. But had the door never been opened, it wouldn’t have been a problem.
As I said at the beginning, reading Dataverse via the Power Pages Web API has its place. For developers new to Power Pages, they may be already familiar with the Dataverse Web API, on which the Power Pages Web API is based. This helps reduce the learning curve of building web applications with Power Pages.
I also think that in situations where the Web API is being used to create, update or delete data, it may make sense from a code consistency standpoint to also use it for read operations. In this case, the Web API must be enabled for the table, so the door needs to be opened anyways.
I also appreciate that it can be more work to use Liquid instead of the Web API. In the case of the custom web service mentioned above, you need to create a web page, a page template, a web template, and you need to write the Liquid code for the service itself. Certainly more work than just calling the out-of-the-box API, but I think after you’ve done it once, you’ll see it’s not really very complicated.
So despite the fact that using Liquid and FetchXML might be a bit more work, I think it’s important to make an informed decision about the risks that come with opening the Web API door. It is almost certainly a bigger door than using Liquid and FetchXML, so I suggest only opening when necessary, and defaulting to Liquid and FetchXML whenever possible.