https://github.com/riskiofr/idempotency-module
Zend Framework module ensuring at most once requests for mutating endpoints
https://github.com/riskiofr/idempotency-module
http idempotency mutating-endpoints zend-framework
Last synced: 2 months ago
JSON representation
Zend Framework module ensuring at most once requests for mutating endpoints
- Host: GitHub
- URL: https://github.com/riskiofr/idempotency-module
- Owner: RiskioFr
- License: mit
- Created: 2017-09-15T20:41:02.000Z (over 7 years ago)
- Default Branch: master
- Last Pushed: 2017-09-27T12:50:57.000Z (over 7 years ago)
- Last Synced: 2024-12-30T16:54:36.182Z (4 months ago)
- Topics: http, idempotency, mutating-endpoints, zend-framework
- Language: PHP
- Size: 50.8 KB
- Stars: 1
- Watchers: 3
- Forks: 0
- Open Issues: 1
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
Idempotency module for Zend Framework
=====================================Zend Framework module ensuring at most once requests for mutating endpoints.
[](https://packagist.org/packages/riskio/idempotency-module)
[](LICENSE)
[](http://travis-ci.org/RiskioFr/idempotency-module)
[](https://packagist.org/packages/riskio/idempotency-module)While the inherently idempotent HTTP semantics around PUT and DELETE are a good fit for many API calls, what if we have an operation that needs to be invoked exactly once and no more? An example might be if we were designing an API endpoint to charge a customer money; accidentally calling it twice would lead to the customer being double-charged, which is very bad.
This is where *idempotency keys* come into play. When performing a request, a client generates a unique ID to identify just that operation and sends it up to the server along with the normal payload. The server receives the ID and correlates it with the state of the request on its end. If the client notices a failure, it retries the request with the same ID, and from there it’s up to the server to figure out what to do with it.
Stripe describes its solution in a [blog post](https://stripe.com/blog/idempotency) which provided the inspiration for this package.
## Requirements
* PHP 7.0+
* [symfony/cache ^3.0](https://github.com/symfony/cache)
* [zendframework/zend-diactoros ^1.1](https://github.com/zendframework/zend-diactoros)
* [zendframework/zend-eventmanager ^2.6.3 || ^3.0](https://github.com/zendframework/zend-eventmanager)
* [zendframework/zend-http ^2.6](https://github.com/zendframework/zend-http)
* [zendframework/zend-mvc ^3.0](https://github.com/zendframework/zend-mvc)
* [zendframework/zend-psr7bridge ^1.0](https://github.com/zendframework/zend-psr7bridge)
* [zendframework/zend-stdlib ^2.7.3 || ^3.0](https://github.com/zendframework/zend-stdlib)
* [zendframework/zend-validator ^2.0](https://github.com/zendframework/zend-validator)## Installation
Idempotency module only officially supports installation through Composer. For Composer documentation, please refer to
[getcomposer.org](http://getcomposer.org/).You can install the module from command line:
```sh
$ composer require riskio/idempotency-module
```After installation of the package, you have to copy the `riskio_idempotency.global.php.dist` file into
your `config/autoload` folder and apply any setting you want.- source: `vendor/riskio/idempotency-module/config/riskio_idempotency.global.php.dist`
- destination: `config/autoload/riskio_idempotency.global.php`### Zend Framework
In Zend Framework application, you must enable the module by adding `Riskio\IdempotencyModule` in your `application.config.php` file.
### Zend Expressive
In Zend Expressive application, you must add the `Riskio\IdempotencyModule\IdempotencyMiddleware` middleware in `config/pipeline.php` file at the very first pipeline records.
```php
pipe(IdempotencyMiddleware::class);
```## Documentation
The module goal is to guarantee the safety of operations on mutating endpoints by allowing clients to pass a unique value with the special **Idempotency-Key** header.
The clients are in charge of creating the unique keys. The module always send back the same response for requests made with the same key, and keys can't be reused with different request parameters.
### Idempotency key format validation
By default, the module uses V4 UUIDs but you can change validation rules using the config file seen above:
```php
[
'idempotency_key_validator' => UuidValidator::class,
],
];
```
### Storage of already performed requestsIn order to keep track of requests performed with **Idempotency-Key** header, you have to specify a PSR-6 cache compliant service. Keys will expire after a delay related to the lifetime of your cache.
```php
[
'cache' => NullCacheAdapter::class,
],
];
```> For instance, Stripe has defined that the keys expire after 24 hours.
### Idempotent requests serialization/deserialization
For each idempotent request, the module creates an instance of ```Riskio\IdempotencyModule\IdempotentRequest``` that contains a checksum of the HTTP request performed and the related HTTP response in order to be stored and eventually retrieved in the future if the request is made more than once.
Accordindly, we must provide a serializer that will serialize and unserialize this class. By defaut, the module uses the ```Riskio\IdempotencyModule\Serializer\Serializer``` class but you can provide another one if you want.
```php
[
'serializer' => Serializer::class,
],
];
```### Eligible HTTP methods
RFC7231 (substituting RFC2616) added two notions to HTTP methods:
* A safe HTTP method is a method that do not modify resources. For instance, using **GET** or **HEAD** on a resource URL, should NEVER change the resource. However, this is not completely true. It means: it won't change the resource representation. It is still possible, that safe methods do change things on a server or resource, but this should not reflect in a different representation.
* An idempotent HTTP method is a method that can be called many times without different outcomes. It would not matter if the method is called only once, or ten times over. The result should be the same. For subsequent calls, the answer will be the same all the time.Method | Safe | Idempotent
------------ | ------------- | -------------
GET | yes | yes
HEAD | yes | yes
POST | no | no
PUT | no | yes
PATCH | no | no
DELETE | no | yesAccordingly to the RFC, only **POST** and **PATCH** HTTP methods are not idempotent. Consequently, the module allows clients to use **Idempotent-Key** header only for these methods by default. However, you can configure another HTTP method list to meet your needs as below:
```php
[
'http_methods' => ['PATCH', 'POST'],
],
];
```### Idempotency key header customization
By default, the module uses **Idempotency-Key** header to submit unique tokens that guarantee idempotency of endpoints. If you want to use another header name for whatever reason, you can specify it as below:
```php
[
'idempotency_key_header' => 'Idempotency-Key',
],
];
```## Testing
``` bash
$ vendor/bin/phpspec run
```## Credits
- [Nicolas Eeckeloo](https://github.com/neeckeloo)
- [All Contributors](https://github.com/RiskioFr/idempotency-module/contributors)## License
The MIT License (MIT). Please see [License File](https://github.com/RiskioFr/idempotency-module/blob/master/LICENSE) for more information.