Scripting custom REST APIs with Dreamfactory
So in my last article I talked about how to get Dreamfactory running within a Docker container with having immutability and horizontal scalability in mind and being able to use it with a Docker orchestrator like Mesos, Marathon, AWS ECS etc.
This time I’ll go into some details about extending it with custom logic / API endpoints as you certainly dont want to let API users access all of the auto-generated API endpoints in regard of security or they simply lack logic.
Dreamfactory has a good role-based ACL which works just fine for several service-types (e.g. MySQL DBs) where you can define server-side filters (e.g. by email from their session) on a record-level which allows users to only work on records belonging to them.
However the roles have their limits. But this is only one reason for adding custom services. You can script your own services aswell as “hook” into existing (auto-generated) services pre/post-process.
Scenario
So assume the scenario where you have a service or data-source which doesnt have authorization or its insufficient for letting untrusted users use it as is.
I had to deal with a 3rd-party SOAP service (sigh) where you could only limit users to “tables” but you cannot use Dreamfactory server-side filters to limit it further down to our needs.
So what I did was creating a SOAP service in DF and only having a single master user for it which credentials I set as global lookup keys. (PS: SOAP WSDL is cached for 1 day by default, see dreamfactory/issues/66 )
(However this is only one of many scenarios where you want to have custom business logic and therefore custom scripts)
Access Control
Next, I created a role for the (untrusted) mobile users and set the access “Requestor” for the SOAP service to only SCRIPT and access to the (yet to be created) custom service to SCRIPT + API.
SCRIPT means, the service is only accessible by server-side scripts which are under YOUR control. API means any (authenticated) user having this role can access all endpoints !
This is only a small aspect of ACL and security in DF and there is more you wanna do for securing your API. I recommend reading their Wiki about Authentication and Authorization. (Maybe I’ll write some more about this in another article)
Now, for the custom service I chose PHP (despite not being my favorite) for a couple of architectural reasons but you can aswell use V8Js, NodeJS or Python. See their Wiki for more valueable information.
Their custom scripting examples are a good starting point / stub.
Caching in Custom Services
As the remote service needs authentication, I needed a way to avoid race-conditions when logging in for every call which would also cause massive network overhead resulting in worse performance.
So how to cache things in your custom scripts ? Use some sane memory-based data-store like Redis or Memcache !
Fortunately DF itself can use Redis or Memcache which you can leverage from custom PHP scripts easily as those have the advantage of accessing any other PHP classes installed in the system. And DF itself is written in PHP with Laravel Framework.
So what I ended up with is a simple yet effective snippet for caching the login token. But it works with any data obviously
$redis = Redis::connection();
$sessionExists = $redis->exists("magento_session");
if($sessionExists) {
$magentoSession = $redis->get("magento_session");
} else {
// call magento/login to get session
$data = [
'username' => '{magento_api_user}',
'apiKey' => '{magento_api_key}'
];
$res = $platform['api']->post->__invoke("magento/login", $data);
$magentoSession = $res['content']['result'];
$redis->set("magento_session", $magentoSession);
$redis->expire("magento_session", 3500);
}
// sorry for bad indentation..stupid bug with some plugin in Wordpress
The first line is a neat trick I found which works because of the Aliases defined in here https://github.com/dreamfactorysoftware/dreamfactory/blob/master/config/app.php#L167-L202 and saves us the “hassle” of setting up the connection ourselves but rather using the existing connection by DF. However prefix your keys with sth random or your company name so you cant interfere with DF keys.
The username and apiKey values are a reference on the lookup keys I mentioned earlier.
Calling other services
When doing custom scripted services you will likely want to wrap / proxy to other services and add some logic on top.
With Dreamfactory its really easy to call other services configured in Dreamfactory. For this purpose DF passes two objects into your script (no matter which language):
- $platform , with 3 sub-objects, giving you access to the REST “api”, “session” with logged-in user information and “config” for retrieving config info of your DF instance
- $event, with 2 sub-objects, giving you access to the “request” and “response” of the REST call, e.g. HTTP
if(is_numeric($payload['replaceId'])) {
$data = [
'sessionId' => $magentoSession,
'productId' => $payload['replaceId']
];
$res = $platform['api']->post->__invoke("magento/catalogProductDelete", $data);
if($res['content']['result'] == FALSE) {
$event['response']['status_code'] = 400;
$event['response']['content'] = ['error' => $res['content']['error']['message'], 'code' => 1000];
return;
}
}
You can see how to access (POST) data payload via the $event object and how easy it is to call other services via the $platform object.
In case you need to error out (or manipulate the response in other fancy ways) you can utilize the $event[‘response’] object.
Have a look at their Wiki on Scripting for a complete overview.
This shall be it for giving you an overview about scripting. Their Wiki is a great source for mostly all information you will need.
On a side-note, I want to send some shouts out to the guys at Dreamfactory who are really helpful, react fast and are nice to work with. So opening GitHub issues and PRs is working out great !
No comments yet.