API service > Initial setup
- Configuration
- Add to container
- Database migration and seeding
- Routes
- Setup events
- Scheduled jobs
- Exception handler
This service requires a configuration array.
Typically, this would be placed at config/api.php.
Example:
use Bayfront\Bones\Application\Utilities\App;
use Bayfront\BonesService\Rbac\RbacService;
return [
'version' => '1.0.0', // API version
'request' => [
'headers' => [ // Required headers for every request
'Accept' => 'application/json',
],
'https_env' => [ // App environments to force HTTPS
App::ENV_STAGING,
App::ENV_QA,
App::ENV_PROD,
],
'id' => [ // Unique request ID
'enabled' => true,
'length' => 10,
],
'ip_whitelist' => [], // Only allow requests from IPs (empty array to allow all)
'meta' => [ // Allow requests for meta array to be returned
'enabled' => true,
'field' => 'meta',
'env' => [
App::ENV_DEV,
App::ENV_STAGING,
App::ENV_QA,
App::ENV_PROD,
],
],
],
'response' => [
'headers' => [ // Required headers for every response
'Content-Type' => 'application/json',
],
],
'rate_limit' => [ // Rate limit (per minute), 0 for unlimited
'auth' => App::environment() === App::ENV_DEV ? 0 : 3,
'private' => App::environment() === App::ENV_DEV ? 0 : 200,
'public' => App::environment() === App::ENV_DEV ? 0 : 10,
],
'identity' => [ // Allowed identification methods
'key' => true, // API key
'token' => true, // Access token
],
'auth' => [ // Allowed authentication methods
'password' => [ // Authenticate with email + password
'enabled' => true,
'tfa' => [
'enabled' => App::environment() !== App::ENV_DEV,
'excluded_domains' => [], // Domains to exclude from TFA requirement (e.g. "example.com")
'wait' => 3, // Wait time (in minutes) to wait before creating a new TFA, or 0 to disable
'duration' => 15, // Validity duration (in minutes), 0 for unlimited
'length' => 6, // Value length
'type' => RbacService::TOTP_TYPE_NUMERIC, // Value type
],
],
'otp' => [ // Authenticate with email + OTP
'enabled' => App::environment() !== App::ENV_DEV,
'excluded_domains' => [], // Domains to exclude from OTP requirement (e.g. "example.com")
'wait' => 3, // Wait time (in minutes) to wait before creating a new TFA, or 0 to disable
'duration' => 15, // Validity duration (in minutes), 0 for unlimited
'length' => 6, // Value length
'type' => RbacService::TOTP_TYPE_NUMERIC, // Value type
],
'refresh' => [ // Authenticate using refresh token
'enabled' => true,
],
],
'meta' => [ // Meta validation rules, or empty for none. Only these keys will be allowed.
'tenant' => [
'address_street' => 'isString|lengthLessThan:255',
'address_street2' => 'isString|lengthLessThan:255',
'address_city' => 'isString|lengthLessThan:255',
'address_state' => 'isString|lengthLessThan:255',
'address_zip' => 'isString|lengthLessThan:255',
],
'user' => [
'name_first' => 'required|isString|lengthLessThan:255',
'name_last' => 'required|isString|lengthLessThan:255',
],
],
'user' => [
'allow_register' => false, // Allow public user registration?
'allow_delete' => true, // Allow users to delete their own accounts?
'impersonation' => [
'enabled' => true, // Enable user impersonation?
'admin_only' => true // Only admins can impersonate?
],
'password_request' => [ // Password reset request
'enabled' => true,
'wait' => 3,
'duration' => 15,
'length' => 36,
'type' => RbacService::TOTP_TYPE_ALPHANUMERIC,
],
'unverified' => [
'expiration' => 10080, // If RBAC users require verification, duration before unverified users are deleted (in minutes). 0 to disable. 10080 = 7 days
'new_only' => true, // Remove only new unverified users? When false, all unverified users will be eligible for deletion
],
'verification' => [ // User email verification
'enabled' => App::environment() !== App::ENV_DEV,
'wait' => 3,
'duration' => 1440,
'length' => 36,
'type' => RbacService::TOTP_TYPE_ALPHANUMERIC,
],
],
'tenant' => [
'allow_create' => false, // Allow non-admin users to create tenants?
'auto_enabled' => true, // Enable tenants created by non-admin users?
'allow_delete' => true, // Allow non-admin users to delete tenants they own?
'user_meta' => [
'manage_self' => true, // Allow tenant users to manage their own tenant_user_meta?
],
],
];version: The API version is added to the information returned by thephp bones about:bonesconsole command. It is also added to the returned meta array whenrequest.meta.enabledistrue.request.headers: API requests without these headers will return aBadRequestException.request.https_env: API requests not made over HTTPS in these environments will return aNotAcceptableException.request.id: When enabled, a uniqueREQUEST_IDconstant is created in theapp.bootstrapevent which can be used to identify each unique request as it is processed by the API. It is added to theErrorResourceschema as well as the returned meta array whenrequest.meta.enabledistrue. The request ID is helpful to attach to any logging services which may be used by the API.request.ip_whitelist: API requests made from an IP not found in this list will return aForbiddenException.request.meta: When enabled, requests with the field value oftrueexisting in the query will return an array of metadata along with the response. For example, by adding?meta=trueto the request, the response will include metadata. This array can be filtered using the api.response.meta filter. Therequest.meta.envarray specifies which app environments to allow this functionality.response.headers: Headers to send with every API response.rate_limit: Define the rate limit for theauth,privateandpublicAPI controllers, or0for unlimited.identity: Allowkeyand/ortokenidentification methods when authorizing aPrivateApiControllerrequest. Thekeymethod will check theX-Api-Keyheader for a valid user API key, and thetokenmethod will check theBearerheader for a valid access token. (See PrivateApiController)auth.passsword: Allow user to authenticate with email + password. Theauth.password.tfaspecifies whether to issue a TFA (two-factor authentication) code, along with its rules.auth.otp: Allow user to authenticate with email + OTP (one-time password), along with its rules.auth.refresh: Allow user to authenticate with a valid refresh token.meta: Themeta.tenantandmeta.userkeys allow for the definition of validation rules enforced for different meta resources. Only defined keys will be allowed.user.allow_register: Allow public user registrationuser.allow_delete: Allow users to delete their own accountsuser.impersonation.enabled: Enable user impersonation?user.impersonation.admin_only: Only admins can impersonate?user.password_request: Allow users to request a password reset, along with its rules.user.unverified.expiration: If RBAC users require verification, duration (in minutes) before unverified users are deleted via a scheduled job.0to disable.user.unverified.new_only: Remove only new unverified users? Whenfalse, all unverified users will be eligible for deletion.user.verification: Require users to verify their email addresses, along with its rules.tenant.allow_create: Allow non-admin users to create tenants?tenant.auto_enabled: Automatically enable tenants created by non-admin users?tenant.allow_delete: Allow non-admin users to delete tenants they own?tenant.user_meta: Allow tenant users to manage their own tenant user meta? If disabled, users must have the necessarytenant_user_meta:*permission.
With the configuration completed, the ApiService class needs to be added to the Bones service container.
This is typically done in the resources/bootstrap.php file.
You may also wish to create an alias.
For more information, see Bones bootstrap documentation.
The ApiService requires the following classes in its constructor:
In addition, the API service makes use of the Leaky Bucket library, so a Bayfront\LeakyBucket\AdapterInterface
must also exist in the container.
By allowing the container to make the class during bootstrapping,
the API service is available to be used in console commands:
$apiService = $container->make('Bayfront\BonesService\Api\ApiService', [
'config' => (array)App::getConfig('api', [])
]);
$container->set('Bayfront\BonesService\Api\ApiService', $apiService);
$container->setAlias('apiService', 'Bayfront\BonesService\Api\ApiService');Since the API service utilizes the RBAC service, the RBAC service migration must be run using:
php bones migrate:upInitial database seeding with a verified admin user and all the necessary permissions can be done from the console:
php bones api:seed user@example.com password
# Force seeding (no input/confirmation required)
php bones api:seed user@example.com password --forceThe password is optional. If not provided, one will be created automatically.
The API service comes will all the routes necessary to utilize all of its features. The use of these predefined routes is optional. You can define your own routes and map them to any of the included controller methods you wish.
To use the API service predefined routes, add the following to an app.bootstrap event subscription
when defining your routes:
use Bayfront\BonesService\Api\Utilities\ApiRoutes
ApiRoutes::define($this->router, '/api/v1');The define method accepts two parameters. The first is a Router instance, which is required.
The second is a route prefix which is automatically added to the beginning of all defined routes.
The API service does not include a route for the root URL. This can be handled at the app-level if desired.
If using any of the API service controllers, the router cannot utilize the class_namespace config key
since the API controllers reside in a different namespace than the controllers used at the app-level.
Some events used by this service would most likely need to dispatch messages. This must be setup on an app-level.
The suggested events are:
api.auth.otpapi.auth.password.tfaapi.user.password.requestrbac.user.password.updatedapi.user.verification.requestrbac.user.verifiedrbac.tenant.invitation.createdrbac.tenant.invitation.accepted
The API service handles all the recommended scheduled jobs by the RBAC service except pruning soft-deleted resources.
Therefore, the following scheduled jobs should be created at the app-level:
- Prune soft-deleted resources which no longer need to exist in the database using the purgeTrashed method.
- Delete expired buckets from storage.
The method to delete the expired buckets will vary based on which storage adapter is being used. Here are some examples:
$this->scheduler->call('delete-expired-buckets', function () {
$files = glob(App::storagePath('/app/buckets/*')); // Path to where the buckets are stored
$count = 0;
foreach ($files as $file) {
if (is_file($file)) {
if (time() - filemtime($file) >= 60 * 60) { // 60 minutes
$count++;
unlink($file);
}
}
}
});$this->scheduler->call('delete-expired-buckets', function () {
// Specify table used for buckets
$this->db->query("DELETE FROM buckets WHERE updated_at < DATE_SUB(NOW(), INTERVAL 60 MINUTE)");
$count = $this->db->rowCount();
});It is recommended to update the app's exception handler to use the ApiError:respond method to ensure all exceptions
thrown will return an ErrorResource.