<?xml version="1.0" encoding="UTF-8"?>
<edmx:Edmx Version="4.0" xmlns:edmx="http://docs.oasis-open.org/odata/ns/edmx">
<edmx:DataServices>
<Schema xmlns="http://docs.oasis-open.org/odata/ns/edm" Namespace="odata4.namespace">
<EntityType Name="Customers"> (1)
<Key> (2)
<PropertyRef Name="CustomerID" />
</Key>
<Property Name="CompanyName" Type="Edm.String" MaxLength="40" Unicode="false" />
<Property Name="ContactName" Type="Edm.String" MaxLength="30" Unicode="false" />
<Property Name="ContactTitle" Type="Edm.String" MaxLength="30" Unicode="false" />
<Property Name="CustomerID" Type="Edm.String" Nullable="false" MaxLength="5" Unicode="false" />
<NavigationProperty Name="Orders" Type="Collection(odata4.namespace.Orders)"/>
</EntityType>
<EntityType Name="Orders">
<Key>
<PropertyRef Name="OrderID" />
<PropertyRef Name="ShipName" />
</Key>
<Property Name="Freight" Type="Edm.Decimal" Precision="19" Scale="4" />
<Property Name="OrderDate" Type="Edm.DateTimeOffset" Unicode="false" />
<Property Name="OrderID" Type="Edm.Int32" Nullable="false" Unicode="false" />
<Property Name="CustomerID" Type="Edm.String" MaxLength="5"/>
<Property Name="Price" Type="Edm.Single" Unicode="false" />
<Property Name="Priority" Type="Edm.Int16" Unicode="false" />
<Property Name="ShipAddress" Type="Edm.String" MaxLength="60" Unicode="false" />
<Property Name="ShipName" Type="Edm.String" Nullable="false" MaxLength="40" Unicode="false" />
<NavigationProperty Name="Customer" Type="odata4.namespace.Customers">
<ReferentialConstraint Property="CustomerID" ReferencedProperty="CustomerID" />
</NavigationProperty>
</EntityType>
<EntityContainer Name="OData4EntityContainer"> (3)
<EntitySet Name="Customers" EntityType="odata4.namespace.Customers"> (4)
<NavigationPropertyBinding Path="Orders" Target="Orders"/>
</EntitySet>
<EntitySet Name="Orders" EntityType="odata4.namespace.Orders"/>
</EntityContainer>
</Schema>
</edmx:DataServices>
</edmx:Edmx>
Building, Implementing, and Testing an OData V4 API
You can query a MySQL database with the OData MySQL example by using an HTTP-based data service.
Before You Begin
You must install the following software to create and use an OData REST API with APIkit:
-
OData Plugin
-
Mule runtime engine 4.3.0 or later
-
Anypoint Studio 7.9.0 or later
Additionally, you must download the OData 4 implementation example.
Scaffold an OData V4 API Using APIkit for OData
-
In Studio, select File > New > Mule Project.
-
In the Project Name field, type the name of your Mule project.
-
Click Finish.
-
In
src/main/resources/api
, create a new file with a.csdl.xml
extension.For example,
odata-metadata.csdl.xml
:1 EntityType
defines the type and its properties.2 key
defines which property or set of properties is the key for the entity.For example, the entity set
Orders
comprises two keys:OrderID
andShipName
.3 EntityContainer
defines the sets of exposed entities.In this example,
Orders
andCustomers
.4 EntitySet
contains a name and EntityType.You must define them in the
schema
namespace. -
In the Package Explorer, right-click your
odata-metadata.csdl.xml
file and select Generate Mule OData 4 API: -
APIkit generates the corresponding flows based on your
.csdl.xml
metadata file.1 The main flow receives messages and routes the operation to the correct handler. 2 Five flows with GET
,PUT
,POST
, andDELETE
operations for each entity.3 A flow with a GET
operation to retrieve the collection of entities.
Handle OData System Query Options
Within each On Entity Request and On Entity Collection Request listener you can access a systemQueryOptions
attribute that maps to each OData query option ($select
, $orderby
, $count
, etc.). For example, to access the $select
value you use the following expression:
#[attributes.odataRequestAttributes.systemQueryOptions.select]
APIkit for OData 4 Implementation Example
-
Download the full apikit-odata4-example.
-
Import the downloaded example in Studio.
-
Run the SQL statements in the src/main/resources/example.sql file to create the database, tables, and test data in MySQL database.
-
Run the application in Studio.
After Studio deploys the application, you can query your OData service using the following query examples:
GET Request Example
curl --location --request GET 'localhost:8081/api/Customers('BLAUS')'
{
"@odata.context": "http://localhost:8081/api/$metadata#Customers/$entity",
"CompanyName": "Blauer See Delikatessen",
"ContactName": "Hanna Moos",
"ContactTitle": "Sales Representative",
"CustomerID": "BLAUS"
}
GET Request Example With Composite Key
curl --location --request GET 'localhost:8081/api/Orders(OrderID=10315,ShipName='Island Trading')
{
"@odata.context": "http://localhost:8081/api/$metadata#Orders/$entity",
"Freight": 41.76,
"OrderDate": "1996-09-26T00:00:00Z",
"OrderID": "10315",
"Price": null,
"Priority": 1,
"ShipAddress": "Garden house Crowther Way",
"ShipName": "Island Trading"
}
Create Example
In this example, the client provides the key (or keys) to create a new entity:
curl --location --request POST 'http://localhost:8081/api/Customers' \
--header 'Content-Type: application/json' \
--data-raw '{
"CompanyName": "Mulesoft",
"ContactName": "Customer 123",
"CustomerID": "NW123"
}'
curl --location --request POST 'http://localhost:8081/api/Orders' \
--header 'Content-Type: application/json' \
--data-raw '{
"Freight": 32.38,
"OrderID": 100000,
"ShipAddress": "Unknown St. 123",
"ShipName": "Order 100000"
}'
Update Example
OData V4 recommends using PATCH to update entities because it only changes the provided fields:
curl --location --request PATCH 'http://localhost:8081/api/Customers('\''BOTTM'\'')' \
--header 'Content-Type: application/json' \
--data-raw '{
"ContactName": "James Bottom"
}'
curl --location --request PATCH 'http://localhost:8081/api/Orders(OrderID=10251,ShipName='\''Victuailles en stock'\'')' \
--header 'Content-Type: application/json' \
--data-raw '{
"ShipAddress": "Unknown Av. 1234"
}'
OData V4 discourages the usage of PUT to update your entities because PUT replaces the entire entity with the new one, which can lead to data loss.
curl --location --request PUT 'http://localhost:8081/api/Customers('\''LONEP'\'')' \
--header 'Content-Type: application/json' \
--data-raw '{
"CompanyName": "New Lonesome Pine Restaurant",
"ContactName": "Fran C. Wilson",
"CustomerID": "LONEP"
}'
curl --location --request PUT 'http://localhost:8081/api/Orders(OrderID=11056,ShipName='\''Eastern Connection'\'')' \
--header 'Content-Type: application/json' \
--data-raw '{
"Freight": 27.52,
"OrderDate": "1998-05-28T00:00:00",
"OrderID": 11056,
"Priority": 2,
"ShipAddress": "45 King George",
"ShipName": "Eastern Connection"
}'
Delete Example
To delete the entity:
curl --location --request DELETE 'http://localhost:8081/api/Customers('\''NW123'\'')'
curl --location --request DELETE 'http://localhost:8081/api/Orders(OrderID=11056,ShipName='\''Eastern Connection'\'')'
Client-Side Pagination in APIkit for OData 4
You can configure your client to request the page using the HTTP query parameters $skip
and $top
.
-
The
$top
system query parameter specifies a non-negative integer n that limits the number of items returned from a collection. -
The
$skip
system query parameter specifies a non-negative integer n that excludes the first n items of the queried collection from the result.
For example:
curl -X GET ‘localhost:8081/api/Customers?$skip=1&$top=5’
Returns five records starting from the second position.
Server-Side Pagination in APIkit for OData 4
Server-side pagination enables you to control a subset of data requests fetched from a client-side server by using a fixed pageSize
parameter. It divides the response data set into discrete pages to improve data readability. The server defines the page size, which is the maximum number of records that the server returns in a request.
Server-Side Pagination Operation in OData 4
OData defines a @odata.nextLink
to indicate that a response is only a subset of the requested collection of entities. As such, @odata.nextLink
contains a URL that enables the client to retrieve the next subset of the requested collection. The URL contains a query parameter $skiptoken
that indicates the server where the next page starts.
Implement Server-Side Pagination in APIkit for OData 4
You can implement the server-side pagination by defining a set of fields with <apikit-odata:request-entity-collection-listener>
in the Responses tab in Anypoint Studio.
After pageSize
is populated, APIkit creates the XML representation of the previous fields:
<apikit-odata:request-entity-collection-listener config-ref="odata-metadata-config" path="/Orders" method="GET" >
<apikit-odata:collection-success-response >
<apikit-odata:entity-collection-success-response pageSize="${service.orders.pageSize}"/>
</apikit-odata:collection-success-response>
<apikit-odata:request-entity-collection-listener>
Page Size or Token Fields for Server-Side Pagination
Page size
is a fixed number that represents the maximum number of records the request-entity-collection
listener can retrieve for each request.
In case you cannot use a numeric pagination, set a token in the Token field
, so a developer can generate it in the corresponding flow.
APIkit for OData includes this token value in the @odata.nextLink
URL with a query parameter $skiptoken=<Token_Value>
.