Summary

Have you ever struggled with ever changing and complicated client requirements? Has querying and presenting the same data in different ways proven to be a complicated task? Is your project manager unable to find enough time for you to develop these requirements and also be satisfied with the code? Well a solution that will make both the developers and the managers happy exists, it’s the OData protocol – achieve more with less! 

The subject will be described in two articles. In the first article the key features of this protocol will be described and in the second article a step by step guide to an ASP .Net Core implementation will be shown.

Introduction

Every software product has a development lifecycle which can have multiple iterations. During this lifecycle from design through development and down to maintenance the software product is evolving according to requirements that come up over time. The famous software principle says “The only constant is change” meaning that the only thing we can be certain of is that nothing is forever and software should always be developed with that in mind.

The Open Data Protocol (OData) is an ISO/IEC approved, OASIS standard that defines a set of best practices for building and consuming RESTful APIs. These best practices facilitate the creation of web services which can easily adapt to change.

OData protocol 

 

OData was initiated by Microsoft in 2007. and the first 3 versions were issued under the “Microsoft Open Specification Promise”. Version 4.0 was standardized at OASIS In 2014. It is a protocol that enables Web clients to communicate and consume REST resources through simple HTTP messages. OData is built on top of standard protocols HTTP, JSON and ATOM while taking advantage of resource representational style described by REST.

Essentially, OData is defining a way of querying APIs that give the Client a feeling of directly accessing the database. That is achieved with a special query syntax by which the Client can filter, paginate, order, reshape or restructure the data in the same way as it would be done through a database manager. Even though the protocol itself is not related to any database, its syntax mostly resembles SQL.

This is achieved by defining an OData model. Every OData service has an OData Entity Data Model attached to it that describes what kind of data can be returned to the client. Based on this model the query syntax is validated, so for example if the Client tries to filter a collection on a property that is not defined in the model, that will result in a Bad Request. This data model defines entities and relations between entities for a specific OData service.

If for example we have an OData Web Service with a URI www.web-service.com, all OData routes are most commonly defined with an “OData prefix” like this: www.web-service.com/odata/{EntityName}. The XML representation of the EDM model can be seen by querying the metadata endpoint which is present by default on every OData service. A GET request to this URL www.web-service.com/odata/$metadata would return an XML document which describes the whole EDM model. Apart from the definition of properties that exist on entities, entity relations are also described in the document.

Simple querying of OData entities is not different from querying any other RESTful service. For example, if we have an entity called “Person”, getting a collection of all Person entities from the OData service would be done just by sending a GET request to the Person resource URL www.web-service.com/odata/Person and each entity from the OData model has its own route. OData also enables querying single entities which have a unique identifier, a key, defined on the entity itself. That request would be sent to this URL www.web-service.com/odata/Person(‘123’)  for a Person with a key ‘123’. 

OData query options 

 

URL conventions specified in the OData protocol are where this technology really shines. Specially formed URL parameters represent actions that will eventually be done on the data itself. These parameters are actually methods that receive the parameter value as input and they are executed on the data collection that is retrieved. For example, if we take our dummy web service and the entity Person and say that that entity has a “FirstName” property, then we can retrieve all the Person entities with the first name “John” by sending a GET request on this URL www.web-service.com/odata/Person?$filter=FirstName eq ’John’ (URL is encoded). 

OData syntax URL parameters are always starting with a ‘$’, in this case it’s the filter parameter. The value of this parameter is the condition by which the Person entities will be filtered. Apart from logical conditions for property values (eq – equal, gt – greater than…), filter function accepts also a wide range of methods for data manipulation like startswith (checks if a string starts with the given value) or arithmetical operations for numbers. The complete list of options for filters can be found in the official protocol documentation, there is no need to name them all here, the example is just to show how many possibilities exist with this filter method.

 

Apart from filter, the user of the API can also use expand, select, orderby, top and skip. This is not the complete list of query options but these are most commonly used.

The expand option is used if the user of the API wants to retrieve entities that are related via a navigational property to the entity he is querying. For example, if a Person is employed then that Person has an Employee navigational property. So, if a GET request would be sent to this URL www.web-service.com/odata/Person?$expand=Employee a Person entity would be returned along with a populated Employee property. Also, all connected entities can be used inside other URL options like filtering on an extended entity’s property.

The select option is used if the Client wants to specify exactly which properties of the queried entity he wants to retrieve. In other words, if a GET request would be sent to this URL www.web-service.com/odata/Person?$select=FirstName, only the first names of all Person entities would be retrieved. When the select option is not specified, all entity properties are returned.

The orderby property is used for ordering the result by a specific property or expression. For example, if a GET request would be sent to this URL www.web-service.com/odata/Person?$orderby=FirstName the resulting collection would be ordered by the Person’s first name in ascending direction. The property name could also be replaced by an expression so that the entities are ordered by the result of that expression.

Top and skip options are pretty similar to the same options in SQL. Top option is receiving a numerical value that specifies which number of entities from the top should the query return (www.web-service.com/odata/Person?$top=20). Skip option is also receiving a numerical value which is saying how many entities from the top should be skipped (www.web-service.com/odata/Person?$skip=20). 

Adapting to change 

 

With all the mentioned options it is clear why it is said that OData “brings the database to the web”. The consumer of the API can, with all the mentioned options and more, form and execute queries as if he is writing them in a database manager program. APIs created like this can easily adapt to any change in the current consumer requirement or handle new ones. For example, one API OData endpoint for a Person entity can be used in all places in a web application where data from that entity should be shown. A list of Persons can be filtered using the filter option, a set of properties that should be shown in the list or a form can be changed by using the select option etc. All of this requires no change on the server side of the application. This is the greatest advantage of an OData API, when an endpoint is set up, all the functionalities are present and any functional change of the behavior can be done by the API’s consumer himself. This is how you can achieve more with less!

On the other hand OData’s greatest advantage is at the same time it’s disadvantage. To be able to effectively use an OData API, the consumer of that API needs to know and understand the protocol and the syntax. Since OData is not established as a standard protocol which most people use, it is likely that consumers will need to learn about the protocol and it is not ideal to put new requirements on the consumers of the API. 

 

Since OData is a protocol, it is not related to any technology stack and the consumer of the API and the API itself do not have to be implemented in the same technology. OData libraries are implemented in several languages and in the next part of the article we will look over the implementation in ASP .Net Core. 

 

Ognjen Stojanović
Senior Software Developer at Northprim