Operations
hydrus provides an easy way for an user to implement functions like CRUD operations and searching on the resources the user has defined in the ApiDoc.
This page explains how a user can interact with an hydrus interface for executing these functions on to any hydrus-deployed API.
How to set up the hydrus server?
After following the instructions on installing hydrus from here, you would just need to execute:
hydrus serve --no-auth
- For the purpose of this tutorial, the ApiDoc we will be using is the default hydrus uses is which is an example Drone ApiDoc which can be found here.
- Also, the
--no-auth
flag is to disable authentication on our hydrus server, as the purpose of this tutorial is to understand the interface between hydrus and the user, and not get into how authentication works in hydrus. For more information on how authentication works in hydrus, check out Authentication.
If the above command executed successfully, then you should have a hydrus server running on port 8080 of your local machine. Now, fetching http://localhost:8080/serverapi
on a browser or an tool like Postman should show you:
{
"@context": "/serverapi/contexts/EntryPoint.jsonld",
"@id": "/serverapi",
"@type": "EntryPoint",
"Area": "/serverapi/Area",
"Command": "/serverapi/Command",
"Datastream": "/serverapi/Datastream",
"Drone": "/serverapi/Drone",
"LogEntry": "/serverapi/LogEntry",
"Message": "/serverapi/Message",
"State": "/serverapi/State",
"collections": [
{
"@id": "/serverapi/MessageCollection",
"@type": "Collection",
"manages": {
"object": "http://localhost:8080/serverapi/vocab#Message",
"property": "http://localhost:8080/serverapi/vocab#type"
},
"supportedOperation": [
{
"@id": "_:messagecollection_retrieve",
"@type": "http://schema.org/FindAction",
"description": "Retrieves all the members of MessageCollection",
"expects": null,
"expectsHeader": [],
"method": "GET",
"possibleStatus": [],
"returns": "http://localhost:8080/serverapi/vocab#Message",
"returnsHeader": []
},
{
"@id": "_:messagecollection_create",
"@type": "http://schema.org/AddAction",
"description": "Create new member in MessageCollection",
"expects": "http://localhost:8080/serverapi/vocab#Message",
"expectsHeader": [],
"method": "PUT",
"possibleStatus": [
{
"@context": "http://www.w3.org/ns/hydra/context.jsonld",
"@type": "Status",
"description": "A new member in MessageCollection created",
"statusCode": 201,
"title": ""
}
],
"returns": "http://localhost:8080/serverapi/vocab#Message",
"returnsHeader": []
},
{
"@id": "_:messagecollection_update",
"@type": "http://schema.org/UpdateAction",
"description": "Update member of MessageCollection ",
"expects": "http://localhost:8080/serverapi/vocab#Message",
"expectsHeader": [],
"method": "POST",
"possibleStatus": [
{
"@context": "http://www.w3.org/ns/hydra/context.jsonld",
"@type": "Status",
"description": "If the entity was updatedfrom MessageCollection.",
"statusCode": 200,
"title": ""
}
],
"returns": "http://localhost:8080/serverapi/vocab#Message",
"returnsHeader": []
},
{
"@id": "_:messagecollection_delete",
"@type": "http://schema.org/DeleteAction",
"description": "Delete member of MessageCollection ",
"expects": "http://localhost:8080/serverapi/vocab#Message",
"expectsHeader": [],
"method": "DELETE",
"possibleStatus": [
{
"@context": "http://www.w3.org/ns/hydra/context.jsonld",
"@type": "Status",
"description": "If entity was deletedsuccessfully from MessageCollection.",
"statusCode": 200,
"title": ""
}
],
"returns": "http://localhost:8080/serverapi/vocab#Message",
"returnsHeader": []
}
],
"title": "messagecollection"
},
....
rest of the collections
....
]
}
Supported operations on a Resource
The supported operations (GET/PUT/POST/DELETE) can be found at by supportedOperation key in the ApiDoc for the required resource. For example, the operations supported by MessageCollection resource can be found here.
...
"@id": "http://localhost:8080/api/vocab#MessageCollection",
"@type": "Collection",
"description": "A collection of messages",
"manages": {
"object": "http://localhost:8080/api/vocab#Message",
"property": "rdfs:type"
},
"subClassOf": "http://www.w3.org/ns/hydra/core#Collection",
"supportedOperation": [
{
"@id": "_:MessageCollection_retrieve",
"@type": "http://schema.org/FindAction",
"description": "Retrieves all the members of MessageCollection",
"expects": "null",
"expectsHeader": [],
"method": "GET",
"possibleStatus": [],
"returns": "http://localhost:8080/api/vocab#Message",
"returnsHeader": []
},
{
"@id": "_:MessageCollection_create",
"@type": "http://schema.org/AddAction",
"description": "Create new member in MessageCollection",
"expects": "http://localhost:8080/api/vocab#Message",
"expectsHeader": [],
"method": "PUT",
"possibleStatus": [
{
"@context": "http://www.w3.org/ns/hydra/context.jsonld",
"@type": "Status",
"description": "A new member in MessageCollection created",
"statusCode": 201,
"title": ""
}
],
"returns": "http://localhost:8080/api/vocab#Message",
"returnsHeader": []
},
{
"@id": "_:MessageCollection_update",
"@type": "http://schema.org/UpdateAction",
"description": "Update member of MessageCollection ",
"expects": "http://localhost:8080/api/vocab#Message",
"expectsHeader": [],
"method": "POST",
"possibleStatus": [
{
"@context": "http://www.w3.org/ns/hydra/context.jsonld",
"@type": "Status",
"description": "If the entity was updatedfrom MessageCollection.",
"statusCode": 200,
"title": ""
}
],
"returns": "http://localhost:8080/api/vocab#Message",
"returnsHeader": []
},
{
"@id": "_:MessageCollection_delete",
"@type": "http://schema.org/DeleteAction",
"description": "Delete member of MessageCollection ",
"expects": "http://localhost:8080/api/vocab#Message",
"expectsHeader": [],
"method": "DELETE",
"possibleStatus": [
{
"@context": "http://www.w3.org/ns/hydra/context.jsonld",
"@type": "Status",
"description": "If entity was deletedsuccessfully from MessageCollection.",
"statusCode": 200,
"title": ""
}
],
"returns": "http://localhost:8080/api/vocab#Message",
"returnsHeader": []
}
],
"supportedProperty": [
{
"@type": "SupportedProperty",
"description": "The members of MessageCollection",
"property": "http://www.w3.org/ns/hydra/core#member",
"readable": "false",
"required": "false",
"title": "members",
"writeable": "false"
}
],
"title": "MessageCollection"
...
From the above part of the ApiDoc, we can see that retrieving(GET) instances, adding instances(PUT), updating instances(POST) and deleting instances(DELETE) to the database is allowed for the MessageCollection resource.
Retrieving resource instances from database
The above response shows all the resources with a collection endpoint served by hydrus. To explore more about any resource, visit the resource endpoint as described in the above reponse.
For example, for more information on the MessageCollecion resource, visit http://localhost:8080/serverapi/MessageCollection
.
Therefore, making a GET request to http://localhost:8080/serverapi/MessageCollection
endpoint, leads to response:
{
"@context": "/serverapi/contexts/MessageCollection.jsonld",
"@id": "/serverapi/MessageCollection/",
"@type": "MessageCollection",
"hydra:totalItems": 0,
"hydra:view": {
"@id": "/serverapi/MessageCollection?page=1",
"@type": "hydra:PartialCollectionView",
"hydra:first": "/serverapi/MessageCollection?page=1",
"hydra:last": "/serverapi/MessageCollection?page=1"
},
"members": [],
"search": {
"@type": "hydra:IriTemplate",
"hydra:mapping": [
{
"@type": "hydra:IriTemplateMapping",
"hydra:property": "http://schema.org/Text",
"hydra:required": false,
"hydra:variable": "MessageString"
},
{
"@type": "hydra:IriTemplateMapping",
"hydra:property": "pageIndex",
"hydra:required": false,
"hydra:variable": "pageIndex"
},
{
"@type": "hydra:IriTemplateMapping",
"hydra:property": "limit",
"hydra:required": false,
"hydra:variable": "limit"
},
{
"@type": "hydra:IriTemplateMapping",
"hydra:property": "offset",
"hydra:required": false,
"hydra:variable": "offset"
}
],
"hydra:template": "/serverapi/Message(MessageString, pageIndex, limit, offset)",
"hydra:variableRepresentation": "hydra:BasicRepresentation"
}
}
Some important points, to understand from the response here are:
- The
hydra:totalItems
key has value zero (0
). This means that there are no instances of this resource in the database yet. This makes sense as till now, nothing is added into the database through hydrus. We have only retrieved(GET) data from the server. - The
members
key is an empty array. This would show all the relvant details of members in the database if they had been added.
Adding resource instance to database
To add an instance of a resource served by hydrus, we need to make a PUT request to the corresponding endpoint with some specific data in the body of the request.
For example, to add a Message object to the database, we need to make a PUT request to http://localhost:8080/serverapi/Message
with the body of the request as:
{
"@type": "Message",
"MessageString": "Test Message"
}
- Here
@type
dictates to what resource should hydrus try to add the given data to the database, thus making it a necessary field. MessageString
is just thehydra:variable
name for a necessary field for an object to belong to the Message class.
If the above request succeeded, then you should get a response along the lines of:
{
"@context": "http://www.w3.org/ns/hydra/context.jsonld",
"@type": "Status",
"description": "Object with ID b7551d4f-cc91-484e-91b1-3b527b4899e0 successfully added",
"statusCode": 201,
"title": "Object successfully added"
}
- The response has a
title
key with value “Object successfully added” meaning that our request was successful and we added a Message object to the database. - The long string of alpha-numeric characters is the unique ID of the object we just added to the database.
Retrieving a single resource instance from the database
To get more details of the object stored in the database, make a GET request to endpoint_on_which_object_added/uniqueID
.
Therefore, to get more details on this specific Message just inserted into the database, to make a GET request to http://localhost:8080/serverapi/Message/b7551d4f-cc91-484e-91b1-3b527b4899e0
.
Then the response is:
{
"@context": "/serverapi/contexts/Message.jsonld",
"@id": "/serverapi/Message/b7551d4f-cc91-484e-91b1-3b527b4899e0",
"@type": "Message",
"MessageString": "Test Message"
}
Here, we can see the Message object with the MessageString we put while adding the object to the database.
NOTE
Now, if a request is made to http://localhost:8080/serverapi/MessageCollection
to view all the contents of the database on that Message
resource, the response would contain:
{
"@context": "/serverapi/contexts/MessageCollection.jsonld",
"@id": "/serverapi/MessageCollection/",
"@type": "MessageCollection",
"hydra:totalItems": 1,
"hydra:view": {
"@id": "/serverapi/MessageCollection?page=1",
"@type": "hydra:PartialCollectionView",
"hydra:first": "/serverapi/MessageCollection?page=1",
"hydra:last": "/serverapi/MessageCollection?page=1"
},
"members": [
{
"@id": "/serverapi/Message/b7551d4f-cc91-484e-91b1-3b527b4899e0",
"@type": "Message"
}
],
"search": {
"@type": "hydra:IriTemplate",
"hydra:mapping": [
{
"@type": "hydra:IriTemplateMapping",
"hydra:property": "http://schema.org/Text",
"hydra:required": false,
"hydra:variable": "MessageString"
},
{
"@type": "hydra:IriTemplateMapping",
"hydra:property": "pageIndex",
"hydra:required": false,
"hydra:variable": "pageIndex"
},
{
"@type": "hydra:IriTemplateMapping",
"hydra:property": "limit",
"hydra:required": false,
"hydra:variable": "limit"
},
{
"@type": "hydra:IriTemplateMapping",
"hydra:property": "offset",
"hydra:required": false,
"hydra:variable": "offset"
}
],
"hydra:template": "/serverapi/Message(MessageString, pageIndex, limit, offset)",
"hydra:variableRepresentation": "hydra:BasicRepresentation"
}
}
- The key
hydra:totalItems
has value is now one(1
) which shows that database has1
item(which was inserted with the PUT request). - The key
members
is does not have an empty array as value. It now contains information(@id
) fn the members of that resource in the database.
Updating a single resource instance from the database
Updating a single object from the database is very similar to the way to retrieve that single resource instance.
We need to make a POST request to endpoint_on_which_object_added/uniqueID
.
Therefore, to update this specific Message which that was just inserted into the database, make a POST request to http://localhost:8080/serverapi/Message/b7551d4f-cc91-484e-91b1-3b527b4899e0
.
You should get response as:
{
"@context": "http://www.w3.org/ns/hydra/context.jsonld",
"@type": "Status",
"description": "Object with ID b7551d4f-cc91-484e-91b1-3b527b4899e0 successfully updated",
"statusCode": 200,
"title": "Object updated"
}
To confirm that the object was updated, making a GET request to http://localhost:8080/serverapi/Message/b7551d4f-cc91-484e-91b1-3b527b4899e0
returns the object details with updated values, which shows that the object from the database has been updated.
Deleting a single resource instance from the database
Deleting a single object from the database is very similar to the way to retrieve that single resource instance.
We need to make a DELETE request to endpoint_on_which_object_added/uniqueID
.
Therefore, to delete this specific Message which that was just inserted into the database, make a DELETE request to http://localhost:8080/serverapi/Message/b7551d4f-cc91-484e-91b1-3b527b4899e0
.
You should get response as:
{
"@context": "http://www.w3.org/ns/hydra/context.jsonld",
"@type": "Status",
"description": "Object with ID b7551d4f-cc91-484e-91b1-3b527b4899e0 successfully deleted",
"statusCode": 200,
"title": "Object successfully deleted."
}
To confirm that the object was deleted, making a GET request to http://localhost:8080/serverapi/MessageCollection
returns hydra:totalItems
as 0, which shows that the object from the database has been deleted.
Searching for an instance using hydrus
hydrus provides an interface to search for instances saved in the database out of the box.
For the sake of using searching feature, three instances of Message class have been inserted in the database using the PUT request with the MessageString property as “First”, “Second” and “Third”.
Therefore a GET request to http://localhost:8080/serverapi/MessageCollection
, responds with
{
"@context": "/serverapi/contexts/MessageCollection.jsonld",
"@id": "/serverapi/MessageCollection/",
"@type": "MessageCollection",
"hydra:totalItems": 3,
"hydra:view": {
"@id": "/serverapi/MessageCollection?page=1",
"@type": "hydra:PartialCollectionView",
"hydra:first": "/serverapi/MessageCollection?page=1",
"hydra:last": "/serverapi/MessageCollection?page=1"
},
"members": [
{
"@id": "/serverapi/Message/720dfcb6-8283-4486-82bf-115c687700af",
"@type": "Message"
},
{
"@id": "/serverapi/Message/20cfd926-5b56-468f-a4ab-01c0941453ae",
"@type": "Message"
},
{
"@id": "/serverapi/Message/7cd4e1a0-9311-472b-bfba-0859cfffca6e",
"@type": "Message"
}
],
"search": {
"@type": "hydra:IriTemplate",
"hydra:mapping": [
{
"@type": "hydra:IriTemplateMapping",
"hydra:property": "http://schema.org/Text",
"hydra:required": false,
"hydra:variable": "MessageString"
},
{
"@type": "hydra:IriTemplateMapping",
"hydra:property": "pageIndex",
"hydra:required": false,
"hydra:variable": "pageIndex"
},
{
"@type": "hydra:IriTemplateMapping",
"hydra:property": "limit",
"hydra:required": false,
"hydra:variable": "limit"
},
{
"@type": "hydra:IriTemplateMapping",
"hydra:property": "offset",
"hydra:required": false,
"hydra:variable": "offset"
}
],
"hydra:template": "/serverapi/Message(MessageString, pageIndex, limit, offset)",
"hydra:variableRepresentation": "hydra:BasicRepresentation"
}
}
We can clearly see that there are three instances of Message class in the database.
hydrus uses query parameters to search for instances in the database.
Now to search for the instance of the Message class with MessageString as “First”, we need to make a GET request to
http://localhost:8080/serverapi/MessageCollection?MessageString=First
, and then we get the response as
{
"@context": "/serverapi/contexts/MessageCollection.jsonld",
"@id": "/serverapi/MessageCollection/",
"@type": "MessageCollection",
"hydra:totalItems": 1,
"hydra:view": {
"@id": "/serverapi/MessageCollection?MessageString=First&page=1",
"@type": "hydra:PartialCollectionView",
"hydra:first": "/serverapi/MessageCollection?MessageString=First&page=1",
"hydra:last": "/serverapi/MessageCollection?MessageString=First&page=1"
},
"members": [
{
"@id": "/serverapi/Message/720dfcb6-8283-4486-82bf-115c687700af",
"@type": "Message"
}
],
"search": {
"@type": "hydra:IriTemplate",
"hydra:mapping": [
{
"@type": "hydra:IriTemplateMapping",
"hydra:property": "http://schema.org/Text",
"hydra:required": false,
"hydra:variable": "MessageString"
},
{
"@type": "hydra:IriTemplateMapping",
"hydra:property": "pageIndex",
"hydra:required": false,
"hydra:variable": "pageIndex"
},
{
"@type": "hydra:IriTemplateMapping",
"hydra:property": "limit",
"hydra:required": false,
"hydra:variable": "limit"
},
{
"@type": "hydra:IriTemplateMapping",
"hydra:property": "offset",
"hydra:required": false,
"hydra:variable": "offset"
}
],
"hydra:template": "/serverapi/Message(MessageString, pageIndex, limit, offset)",
"hydra:variableRepresentation": "hydra:BasicRepresentation"
}
}
Now, in the response, the number of members is just one. It is the instance of the database with MessageString as “First”.
To confirm this, we can make a GET request to http://localhost:8080/serverapi/Message/720dfcb6-8283-4486-82bf-115c687700af
to dereference that instance, and we get the response as:
{
"@context": "/serverapi/contexts/Message.jsonld",
"@id": "/serverapi/Message/720dfcb6-8283-4486-82bf-115c687700af",
"@type": "Message",
"MessageString": "First"
}
We can clearly see that the “MessageString” value of this instance in “First”, confirming that hydrus as filtered the correct instance.
NOTE: Searching in hydrus is case sensitive.
Therefore, a GET request to http://localhost:8080/serverapi/MessageCollection?MessageString=first
would lead to zero matches.
Collection as a Resource
Making a collection
A collection is just a set of objects logically grouped together. We can also make collection object in the same way we make regular class instances. Let us take the example of github comment and issues to understand the benefit of creating collections.
The advantage of using a collection is that we define a CommentCollection
object, where we link all Comment
objects for a particular issue. We then give that CommentCollection
object as the property for the issue. In terms of defining it in the API documentation, it would be something like:
{
"@type": "SupportedProperty",
"property": vocab:CommentCollection,
"readonly": "false",
"required": "true",
"title": "Comments",
"writeonly": "false"
}
The property would map to an instance of the CommentCollection
class. This will be defined in the supportedProperty
field for the definition of the Issue
class. Like any other object, when we define the Issue
object, we will add the appropriate instance of the CommentCollection
to the Issue instance. So an object would be something like:
{
"@type": "Issue",
"@id": "/api/Issues/27",
"Issue": "....",
"Comments": "/api/CommentCollection/32",
....
}
To create a CommentCollection
, we first need to have comments. For doing that, you would need to do PUT requests to add those comments in the database. Now to group them into a collection, make a PUT request to /serverapi/CommentCollection
endpoint with the body of the request having the ids of the comments which need to be grouped together in this collection, in the following format:
{
"@type": "CommentCollection",
"members": [
{
"@id": "/serverapi/Comment/4ff14a9e-9cd0-4e8a-9c11-86ac9bec211f",
"@type": "Comment"
},
{
"@id": "/serverapi/Comment/aab38f9d-516a-4bb2-ae16-068c0c5345bd",
"@type": "Comment"
}
]
}
This would lead to a response similar to:
{
"@context": "http://www.w3.org/ns/hydra/context.jsonld",
"@type": "Status",
"description": "Object with ID 50ed3b68-9437-4c68-93d4-b67013b9d412 successfully added",
"statusCode": 201,
"title": "Object successfully added."
}
Therefore, 50ed3b68-9437-4c68-93d4-b67013b9d412
is the id
of our collection.
Fetching a collection
Now, GET on CommentCollection/50ed3b68-9437-4c68-93d4-b67013b9d412
would return all members of this particular collection with response like:
{
"@context": "/serverapi/contexts/CommentCollection.jsonld",
"@id": "/serverapi/CommentCollection/50ed3b68-9437-4c68-93d4-b67013b9d412",
"@type": "CommentCollection",
"members": [
{
"@id": "/serverapi/Comment/aab38f9d-516a-4bb2-ae16-068c0c5345bd",
"@type": "hydra:Link"
},
{
"@id": "/serverapi/Comment/4ff14a9e-9cd0-4e8a-9c11-86ac9bec211f",
"@type": "hydra:Link"
}
]
}
The members returned are all hydra:Links
.
NOTE: Operations for updating(POST) and deleting(DELETE) collection is similar to how it happens for normal class instances.