API actions¶
This article would be useful for those who needs to create a custom API action.
Routing¶
You can define a specific route to access your API action (endpoint).
Default routes are defined here: application/Espo/Resources/routes.json.
Custom routes can be defined in the following places:
custom/Espo/Modules/{ModuleName}/Resources/routes.jsoncustom/Espo/Custom/Resources/routes.json
Example¶
routes.json
[
{
"route": "/MyAction",
"method": "get",
"actionClassName": "Espo\\Modules\\MyModule\\Api\\GetMyAction"
},
{
"route": "/MyScope/:id/something",
"method": "post",
"actionClassName": "Espo\\Modules\\MyModule\\Api\\PostMyScopeSomething"
},
{
"route": "/Hello/test/:id",
"method": "get",
"params": {
"controller": "MyController",
"action": "doSomething",
"id": ":id"
}
},
{
"route": "/HelloWorld/:name",
"method": "post",
"params": {
"controller": "MyController",
"action": "helloWorld",
"name": ":name"
}
},
{
"route": "/TestNoAuth",
"method": "get",
"params": {
"controller": "Test",
"action": "test"
},
"noAuth": true
}
]
Route parameters:
- noAuth makes the endpoint accessible without authentication.
- method specifies an HTTP method. The most used methods are: get, post, put, delete.
- actionClassName defines an action class name – the entry point of the route.
- params – used for controller implementation, not recommeded.
A route can contain placeholders (for example, :id). The value will be passed to the Action in the Request object.
A route can be processed either by an Action class or by a Controller.
The API endpoint for a particular route: https://your-site/api/v1/{route}.
Clearing cache is required after changes in routing files.
Action¶
As of v7.4.
Note
This is the preferable method.
The actionClassName parameter in a route definition defines the action class. The action class is an entry point. Action classes should implement the Espo\Core\Api\Action interface.
Example:
<?php
namespace Espo\Modules\MyModule\Api;
use Espo\Core\Api\Action;
use Espo\Core\Api\Request;
use Espo\Core\Api\Response;
use Espo\Core\Api\ResponseComposer;
use Espo\Core\Exceptions\BadRequest;
use RuntimeException;
use Espo\Modules\MyModule\Service;
class GetMyAction implements Action
{
public function __construct(
// Some class we delegate the logic to.
private Service $service,
) {}
public function process(Request $request): Response
{
// A route parameter value is passed in an URI, if defined in the route.
// E.g. `/Hello/:id`.
$id = $request->getRouteParam('id') ?? throw new RuntimeException();
// Read payload.
$someInputValue = $request->getParsedBody()->someKey ??
throw new BadRequest("Key is not passed in payload.");
// Delegate the logic to some class.
$data = $this->service->get($id, $someInputValue);
// Output the result as JSON.
return ResponseComposer::json([
'someKey' => $data->someKey,
]);
}
}
For most cases, the sections below won't be relevant and can be skipped.
Custom controller¶
Note
This is not a preferable method.
Alternative to an Action class. A Controller can contain multiple methods for different actions.
In Custom folder¶
Create a file custom/Espo/Custom/Controllers/MyController.php.
<?php
namespace Espo\Custom\Controllers;
class MyController
{}
Clear cache (Administration > Clear Cache).
In Module folder¶
Create a file custom/Espo/Modules/MyModule/Resources/metadata/scopes/MyController.json.
{
"module": "MyModule"
}
Create a file custom/Espo/Modules/MyModule/Controllers/MyController.php.
<?php
namespace Espo\Modules\MyModule\Controllers;
class MyController
{
// Dependencies are passed to the constructor.
}
Clear cache after creating a new controller (Administration > Clear Cache).
Example:
<?php
namespace Espo\Modules\MyModule\Controllers;
use Espo\Core\Api\Request;
use Espo\Core\Api\Response;
use SomeDependency;
use stdClass;
class MyController
{
public function __construct(private SomeDependency $someDependency)
{}
// Naming convention: `{method}Action{Action}`.
public function putActionUpdate(Request $request, Response $response): stdClass
{
$id = $request->getRouteParam('id');
$data = $request->getParsedBody();
$result = $this->someDependency->doSomething($id, $data);
// Response can be returned or written with `Response::writeBody`.
return $result->toStdClass();
}
}
Extending existing controller¶
Note
Not recommended.
Example for the Account scope.
Create a file (or modify if it already exists) custom/Espo/Custom/Controllers/Account.php.
<?php
namespace Espo\Custom\Controllers;
use Espo\Core\Api\Request;
use Espo\Core\Api\Response;
class Account extends \Espo\Modules\Crm\Controllers\Account
{
/**
* POST api/v1/Account/action/test
*/
public function postActionTest(Request $request, Response $response): bool
{
$someParam = $request->getQueryParam('someParam'); // GET parameter
$data = $request->getParsedBody(); // payload
$someValue = $data->someKey ?? null;
$response->setStatus(201); // example how to set custom response status code
// call some service class here
return $someData; // can be true, false, array or object.
}
/**
* GET api/v1/Account/action/test
*/
public function getActionTest(Request $request, Response $response): void
{
$someParam = $request->getQueryParam('someParam'); // GET parameter
// call some service class here
$response->writeBody('true');
}
}
Note
For the Account entity type, we extend Espo\Modules\Crm\Controllers\Account.
Some entity types might not have controllers in Espo\Modules\Crm\Controllers namespace. They are defined in Espo\Controllers namespace.
CRUD actions¶
If a controller is used, the CRUD operations for a record are implemented using the methods shown below.
<?php
namespace Espo\Custom\Controllers;
use Espo\Core\Api\Request;
use Espo\Core\Api\Response;
class MyController
{
// Creates a record.
public function postActionCreate(Request $request, Response $response): void
{}
// Reads a record.
public function getActionRead(Request $request, Response $response): void
{}
// Updates a record.
public function putActionUpdate(Request $request, Response $response): void
{}
// Deletes a record.
public function deleteActionDelete(Request $request, Response $response): void
{}
}