baraja-core/structured-api

There is no license information available for the latest version (v2.1.1) of this package.

Complex library for definition of your structured API endpoint as class with schema.

v2.1.1 2020-07-30 14:06 UTC

README

Integrity check

Full compatible smart structured API defined by schema.

  • Define full type-hint input parameters,
  • Validate returned data by schema,
  • Full compatible with Nette framework,
  • Inject dependencies by @inject annotation in public property.

📦 Installation & Basic Usage

This package can be installed using PackageRegistrator which is also part of the Baraja Sandbox. If you are not using it, you have to install the package manually following this guide.

A model configuration can be found in the common.neon file inside the root of the package.

To manually install the package call Composer and execute the following command:

$ composer require baraja-core/structured-api

🛠️ API endpoint

API endpoint is simple class with action methods and dependencies. For best comfort please use your custom BaseEndpoint with declaring all required dependencies.

Simple example:

<?php

declare(strict_types=1);

namespace App\Model;


final class MyAwesomeEndpoint extends BaseEndpoint
{

   /**
    * This is test API endpoint as demonstration of inner logic.
    *
    * @param string $hello some user-defined parameter.
    */
   public function actionDefault(string $hello = 'world'): void
   {
      $this->sendJson([
         'name' => 'Test API endpoint',
         'hello' => $hello,
      ]);
   }

   /**
    * @param mixed[] $data
    */
   public function postCreateUser(array $data): void
   {
      $this->sendJson([
         'state' => 'ok',
         'data' => $data,
      ]);
   }
}

Method actionDefault is used for request in format /api/v1/test with query parameter ?hello=....

Method postCreateUser will be called by POST request with all data.

📝 Endpoint registration

If you use the Nette Framework, all endpoints will be registered automatically. This provides an extension for DIC.

To register automatically, simply inherit BaseEndpoint or implement the Baraja\StructuredApi\Endpoint interface.

🌐 HTTP methods

The library supports all HTTP methods for the REST API. The selection of the HTTP method is solved by the method name.

You can specify the HTTP method at the beginning of the method name, or use an alias:

final class MyAwesomeEndpoint extends BaseEndpoint
{
   public function getDefault(): void
   {
      // this logic will be called as GET.
   }

   public function actionDefault(): void
   {
      // this logic will be called as GET too,
      // because `action` prefix is alias for GET.
   }

   public function postDetail(string $id, string $name, ?string $description = null): void
   {
      // this logic will be called as POST.
   }
}

List of aliases (aliases are optional):

  • action as GET
  • update as PUT
  • create as POST

💾 Obtaining raw data

For processing complex data structures, it may be useful to obtain the data in its original raw form.

The library reserves the key variable array $ data, which always contains the original input values from the user, regardless of validation.

For example:

final class OrderEndpoint extends BaseEndpoint
{
   public function postProcessOrder(array $data): void
   {
      // variable $data constains all raw data from user.
   }
}

✅ Validation

In the runtime, when calling methods, the passed arguments against the method definition and data types are validated. This ensures that the endpoint is never called with incorrect data.

The library guarantees that you will always get the data in the type you request. If you need to define the type more complicated, you can use a comment annotation.

Combined example:

final class ArticleEndpoint extends BaseEndpoint
{

   /**
    * @param string|null $locale in format "cs" or "en"
    * @param int $page real page number for filtering, 1 => first page ... "n" page
    * @param int $limit in interval <0, 500)
    * @param string|null $status matching constant self::STATUS_* (null, all, published, trash)
    * @param string|null $query smart search query
    * @param string|null $filterFrom find all articles from this date
    * @param string|null $filterTo find all articles to this date
    * @param string|null $sort sort by supported field
    * @param string|null $orderBy direction by `ASC` or `DESC`
    */
   public function actionDefault(?string $locale = null, int $page = 1, int $limit = 32, ?string $status = null, ?string $query = null, ?string $filterFrom = null, ?string $filterTo = null, ?string $sort = null, ?string $orderBy = null): void
   {
   }
}

The library takes full advantage of PHP 7 and always checks data types. If the data is passed in the wrong type (for example, a boolean cannot be passed by the GET method), it performs an automatic conversion or throws an error.

If the argument contains a default value, it is automatically marked as optional. If the user does not pass a value, the default is used. All mandatory arguments must always be passed, if not, your logic will not be called at all.

🙋 Smart response

The library contains a number of built-in methods for elegant handling of all important return states.

For example, if we want to get the detail of an article by ID and return its detail from the database, the use is completely intuitive:

final class ArticleEndpoint extends BaseEndpoint
{
   public function actionDetail(string $id): void
   {
      // your logic for fetch data from database

      // your response
      $this->sendJson([
         'id' => $id,
         'title' => 'My awesome article',
         'content' => '...',
      ]);
   }
}

Each method always returns a type of void and the output logic is solved via methods. The reason is that it is often necessary to pass a number of parameters and one output is not enough.

Warning: If you do not pass any output, endpoint processing will fail.

When processing actions, it is a good idea to return success or error information:

final class ArticleEndpoint extends BaseEndpoint
{
   public function createDetail(string $title, string $content, ?string $perex = null): void
   {
      try {
         // creating in database...
         $this->sendOk([
            'id' => 123,
         ]);
      } catch (\Exception $e) {
         $this->sendError('Can not create article because ...');
      }
   }
}

🔒 Permissions

🚩Warning: If you do not set the rights at all, by default all endpoints are private and you must log in to call them! 👮

All API requests are validated at runtime. If you want to allow all users access to your endpoints, please add the @public annotation to main class doc comment.

For example (this endpoint will be public):

/**
 * @public
 */
final class ProductEndpoint extends BaseEndpoint
{
}

To restrict rights, define an @role annotation over a class or method.

For example (only administrators and moderators can call this endpoint):

/**
 * @role admin, moderator
 */
final class ArticleEndpoint extends BaseEndpoint
{
}

Rights settings can also be combined. For example, in a public endpoint, restrict rights to a specific method only:

/**
 * @public
 */
final class SitemapEndpoint extends BaseEndpoint
{

   /**
    * @role admin
    */
   public function actionClearCache(): void
   {
      // your secured implementation
   }
}

🗺️ Project endpoint documentation

When developing a real application, you will often need to pass work between the backend and the frontend.

To describe all endpoints, the package offers an optional extension that generates documentation automatically based on real code scanning.

Try the Structured API Documentation.

📄 License

baraja-core/structured-api is licensed under the MIT license. See the LICENSE file for more details.