RESTful Server API
The RESTful server API
This page illustrates how a client can interact with a RESTx server.
Self-discovery is a key aspect of a RESTful architecture. For RESTx this means: A client can connect to the server and easily find out all there is to know about the server, the available components and resources.
RESTx supports discovery and use via a web browser, producing well formatted HTML. However, to illustrate the simplicity of the interface, we can just use telnet to interact with the server. Assume you have a RESTx server running on your machine, listening on port 8001. You can therefore connect with a telnet client, like so:
% telnet localhost 8001 Trying ... Connected to localhost. Escape character is '^]'.
After the connection is established, you enter the HTTP request header, starting with the request method, URI and HTTP version in the first line, for example: GET / HTTP/1.1
Each request header line is ended simply by hitting enter. The next request header line can then be typed. You signal the end of the HTTP request header by hitting Enter twice (entering an empty line). The server then processes the request and sends its response, which you can see returned to you in the telnet session.
In the following examples we omit the 'telnet' line and the 'Trying... Connected... Escape...' lines, which always stay the same. Instead, we only show the request header lines which should be typed in. After hitting enter for an empty line, you receive the answer from the server, which is then shown as well.
Getting the server's home page
Request:
GET / HTTP/1.1 Connection: close Accept: application/json
Response:
HTTP/1.1 200 OK
Content-type: application/json
Content-length: 181
{
"code": "/code",
"doc": "/meta/doc",
"name": "MuleSoft RESTx server",
"resource": "/resource",
"static": "/static",
"version": "0.9"
}Notice how in the request we tell the server that we are looking for JSON content. If this line is missing, the server returns the same information, but HTML formatted, suitable for display to a human in a browser. JSON, however, is easier to process by automated clients.
The respond starts with an ordinary HTTP return header, telling us the status of the request ('Ok'), the content type that is returned (JSON, as requested) and the length of the content. Then, after one empty line, the actual response body starts. As you can see, the information is contained in a JSON formatted data structure of name/value pairs. In many programming languages, a structure like this is known as a map, dictionary or associative array. The RESTx server has returned the high level links about itself, inviting further exploration. There is a link for documentation, the version information, and so on. Let's focus on the link '/code' first. That link is used to find out about the available components.
Find out about the installed components
Request:
GET /code HTTP/1.1 Connection: close Accept: application/json
Response (varies by installed components, response headers omitted):
{
"CombinerComponent": {
"desc": "Calls another component.",
"uri": "/code/CombinerComponent"
},
"GoogleSearchComponent":
"desc": "Provides an interface to Google Search.",
"uri": "/code/GoogleSearchComponent"
},
"TwitterComponent": {
"desc": "Provides access to a Twitter account.",
"uri": "/code/TwitterComponent"
}
}The server returns a slightly more complex data structure: We notice that individual elements in the top-level dictionary or map may contain further dictionaries or maps themselves. We see three different components, each with a brief description and the URI to go to in order to find out more. Let's examine the Twitter component.
Finding out the details about a specific component
Request:
GET /code/TwitterComponent HTTP/1.1 Connection: close Accept: application/json
Response:
{
"desc": "Provides access to a Twitter account.",
"doc": "/code/TwitterComponent/doc",
"name": "TwitterComponent",
"params": {
"account_name": {
"default": null,
"desc": "Twitter account name",
"required": true,
"type": "string"
},
"account_password": {
"default": null,
"desc": "Password",
"required": true,
"type": "password"
}
},
"resource_creation_params": {
"desc": {
"default": "A 'TwitterComponent' resource",
"desc": "Specifies a description for this new resource",
"required": false,
"type": "string"
},
"public": {
"default": false,
"desc": "Indicates whether the resource should be public",
"required": false,
"type": "boolean"
}
"suggested_name": {
"default": null,
"desc": "Can be used to suggest the resource name to the server",
"required": true,
"type": "string"
}
},
"services": {
"status": {
"desc": "You can GET the status or POST a new status to it.",
"input_types": [
"application/json",
"application/x-www-form-urlencoded"
],
"output_types": [
"application/json",
"text/html",
"*/*"
],
"uri": "/code/TwitterComponent/status"
},
"timeline": {
"desc": "You can GET the timeline of the user.",
"output_types": [
"application/json",
"text/html",
"*/*"
],
"uri": "/code/TwitterComponent/timeline"
}
},
"uri": "/code/TwitterComponent"
}The server has returned a component descriptor. There is a lot of useful information here: Name, description, link to documentation, list of parameters ('params') that need to be provided during resource creation time and information about each individual parameter. Those parameters are defined in the source code of the component. There are also 'resource_creation_params', which are the same for all components and are handled by RESTx: The name by which a resource should be known, a small, human readable description, etc.
Finally, we can also find out about 'services', which are service-resources exposed by the component. They also have a description and URI available. If a service should accept parameters then a complete parameter definition may be included for the service as well. Services appear as 'sub-resources', once a resource has been created.
Creating a new resource
To create a new resource, we need to POST the necessary parameters to the component's code URI, for example the one we just accessed. We need to provide values for all the mandatory parameters at the least, but can also provide values for some optional parameters.
Looking at what the component just told us about itself, we can see that the only required parameters are 'account_name' and 'account_password' under the 'params' section of the component descriptor. Under 'resource_creation_params' there are no required parameters, but we want to set our own description and suggest a name. So, we issue the following request:
Request:
POST /code/TwitterComponent HTTP/1.1
Accept: application/json
Content-length: 266
Connection: close
{
"params": {
"account_name": "SomeTwitterAccount",
"account_password": "*********"
},
"resource_creation_params": {
"desc": "A twitter resource for this account.",
"suggested_name": "MyTwitterResource"
}
}Response:
HTTP/1.1 201 Created
Location: /resource/MyTwitterResource
Content-type: application/json
{
"name": "MyTwitterResource",
"status": "created",
"uri": "/resource/MyTwitterResource"
}The response code 201 indicates the successful creation of the new resource. Via the 'Location' header, as well as through the returned content, we are told the new resource's URI. Let's take a look at that URI.
Examining a resource
Request:
GET /resource/MyTwitterResource HTTP/1.1 Connection: close Accept: application/json
Response:
{
"desc": "A twitter resource for this account.",
"name": "MyTwitterResource",
"services": {
"status": {
"desc": "You can GET the status or POST a new status to it",
"input_types": [
"application/json",
"application/x-www-form-urlencoded"
],
"output_types": [
"application/json",
"text/html",
"*/*"
],
"uri": "/resource/MyTwitterResource/status"
},
"timeline": {
"desc": "You can GET the timeline of the user",
"output_types": [
"application/json",
"text/html",
"*/*"
],
"uri": "/resource/MyTwitterResource/timeline"
}
},
"uri": "/resource/MyTwitterResource"
}Very important: This resource descriptor contains only contains information that is relevant to the user of this resource. There is much less information than what we received in the component descriptor. In fact, at this point, it is not even shown which component is behind this resource. Furthermore, all information about the resource-creation time parameters – such as account credentials – are hidden from the user. That way, the implementation details or actual provider components at work behind the scenes may be changed at a later time, without the need to update any client references.
We can see that this resource provides two services, or sub-resources: 'status' and 'timeline'. We get a description and a URI for each of them. Accessing one of them (for example the status) will result in some useful things happening on the server. Let's try it!
Accessing a resource service
Request:
GET /resource/MyTwitterResource/status HTTP/1.1 Connection: close Accept: application/json
Response:
"Gluing data together with #MuleSoft's RESTx"
And this would be the latest Twitter message of the account specified during resource creation time. The user of the resource does not need to know anything about the account details or even that this is a twitter message. We can see that the URI is a completely opaque handle to the data, which may be used in arbitrary contexts.
Conclusion
We can see that the RESTful interactions with the RESTx server are simple and natural. Links to more available data is returned by the server, clients do not need to have prior, hard-coded knowledge about specific URIs. Starting from the root URI, a client can explore the server, the available components and resources.
Given this simple interface, it is possible to develop convenient helper-libraries for clients, which further automate and simplify interactions with the server. The RESTx distribution includes sample client libraries for several languages.