Writing your own components
Writing your own components
RESTx makes it easy to turn your custom code into components. Here is a step by step guide, along with some important guidelines and links to further reading.
1. Create a component template
After an install, you can create a component template for the language of your choice with just a single command:
% ./restxctl component create MyTestComponent [ java | python | javascript ]
Here, 'MyTestComponent' is the name you would like to give your new component, while 'java' , 'python' or 'javascript' indicates the language in which you want to write your component. Currently, these are the three supported languages. More will be added in the future.
=== Creating a new Python component template. Name: MyTestComponent Created new component: /home/user/restx/src/python/restx/components/MyTestComponent.py A server restart is required to enable the component: restxctl restart
As you can see, a new source file was created for you, in the correct location for components written in that particular language. Time to edit your component.
2. Implementing your component
When you open the freshly created component file, you can see the class name is already correctly set. Extensive code comments are provided to lead you through all parts of a component. Please see some even more fully documented examples for Java, Python and JavaScript components. These examples currently represent the most thorough documentation of the component API and should definitely be consulted.
Key considerations and guidelines when writing a component
No constructor: Components should not have a constructor, since component objects are frequently created. In theory, they could have a default constructor, but nothing heavy should take place there. Best to just remember: No constructor.
No long term state in components: A new component instance are created for every access to a resource that is built around a component. Therefore, component instances cannot keep state between several requests to the resource. If you require state to be kept, please use the file storage object. See the documented example components for more details.
Service methods: A component implements 'service methods'. These are functions, whose names are going to be reflected as sub-resources in the publicly accessible interface to any resource that is based on this component. For example, if your component implements service method 'bar' and the URI for a resource is '/resource/foo' then clients will access '/resource/foo/bar' in order to invoke this particular method. Important: A RESTful system exposes resources at URIs, not actions. Therefore, a good rule of thumb is to give nouns as names for your service methods. This will keep you as the developer focused on what it is you are implementing in such a method: The necessary code to produce a particular data item for the user. REST is not RPC.
No request routing based on HTTP method: Contrary to some other frameworks, RESTx only routes requests to service methods based on the URI path and does not take into consideration the HTTP request method (GET, PUT, POST, etc.). The HTTP method is however passed as the first parameter to every service method. It is trivial to invoke particular actions or call specialized methods/functions based on the HTTP method.
Input for service methods: From version 0.9.4 onwards, the acceptable input types for a service method can be specified as part of the service definition. For Java components, an annotation is used. By specifying no particular input types, the default types are accepted ("application/json" and "application/x-www-form-urlencoded"). If an empty list of input types is defined (or NO_INPUT in the case of Java) then no input is allowed at all. Custom types can easily be supported by adding new 'renderers' to the restx.render module. Any data that is sent in the body of a PUT or POST request is considered as input and is automatically converted into a language object based on the content-type header in the client request. This object typically represents a dictionary or map of parameters and values. Input is always passed as the second parameter to a service method.
Service method parameters: A service method can accept additional, ordinary parameters, which it declares and describes to RESTx (by means that are different for each language). These parameters are later provided on the URI command line. For example, if your 'bar' service method accepts numeric parameter 'xyz' then the URI may look like this: '/resource/foo/bar?xyz=123'. However, you can also declare some or all of the service method's parameters as 'positional'. In that case, the URI may just be '/resource/foo/bar/123'. Service method parameters currently can only be of type string, boolean and numeric.
Configuration for components: Components typically don't read configuration files. Instead, the configuration is provided once, at resource creation time, and not every time the URI of a service method is accessed. A component therefore declares the configuration parameters it needs to RESTx. The component APIs for the different languages provide different means to do that. Service methods have access to the component's configuration parameters (provided at resource creation time) just like they would access any fields or attributes of the component object that they are part of.
Output of service methods: Service methods always return a Result object. This can either be created directly, specifying an HTTP status code and return data in the constructor. Or it can be created through a factory method provided by the Result class. These factory methods have names like 'ok', 'created', 'notFound', etc. The status code then is set for you. Please see the documented component examples for a complete list. Data can be either a basic type like string, boolean or numeric, or a map/dictionary or array/list. Those can contain elements of basic types or further maps/dictionaries or arrays/lists. This allows the construction of arbitrary complex, hierarchical documents or record data. RESTx supports content negotiation between client and server and allows the specification of particular output content types in the service definition (or via an annotation in Java). If no such types are defined then the default types of "application/json", "text/html" and "*/*" are supported. The data of the Result object is automatically rendered in the agreed-upon, supported content type. You can also attach custom headers to a Result object with the addHeader() method. If you set the 'Content-type' header then RESTx will not attempt to render the output in a content type based on the client request or any negotiated content type. instead, it just accepts your output data as is, which is ideal for binary, raw output.
Compiling your component code
This step is only necessary for Java components. Python or JavaScript components do not require compilation.
To compile your Java components, issue this command:
./restxctl component compile
Installing / enabling your component
When you created the component template, RESTx was already updated with information about the new component. However, the change only takes effect after a server restart:
./restxctl restart
Disabling / removing a component
Currently, there is no command line utility to remove a component. This still needs to be done manually. These are the steps:
-
Open the src/python/restx_components_list/__init__.py file in an editor and remove the line referring to the component you want to disable.
-
Optional: Remove the component file.
-
Restart the server.
Links
- Java component API documentation
- Python component API documentation
- Documented Java component example
- Documented Python component example
- Documented JavaScript component example