{"id":25952123,"url":"https://github.com/davidwyly/rxn","last_synced_at":"2025-10-26T19:42:22.158Z","repository":{"id":56963536,"uuid":"53752544","full_name":"davidwyly/rxn","owner":"davidwyly","description":"Rxn (Reaction) - A fast, simple, and powerful API framework for PHP","archived":false,"fork":false,"pushed_at":"2018-09-30T07:53:05.000Z","size":459,"stargazers_count":3,"open_issues_count":1,"forks_count":0,"subscribers_count":5,"default_branch":"master","last_synced_at":"2024-04-20T15:51:59.245Z","etag":null,"topics":["api-rest","api-versioning","autoloading","autowiring","backend","crud-api","dependency-injection","frontend-decoupling","json-api","lightweight","method-injection","orm-library","php-framework","php7","rxn","scaffolding","schema-migrations","work-in-progress"],"latest_commit_sha":null,"homepage":"","language":"PHP","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/davidwyly.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2016-03-12T20:44:58.000Z","updated_at":"2021-05-13T22:29:17.000Z","dependencies_parsed_at":"2022-08-21T08:20:55.348Z","dependency_job_id":null,"html_url":"https://github.com/davidwyly/rxn","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/davidwyly%2Frxn","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/davidwyly%2Frxn/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/davidwyly%2Frxn/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/davidwyly%2Frxn/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/davidwyly","download_url":"https://codeload.github.com/davidwyly/rxn/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":241867649,"owners_count":20033815,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["api-rest","api-versioning","autoloading","autowiring","backend","crud-api","dependency-injection","frontend-decoupling","json-api","lightweight","method-injection","orm-library","php-framework","php7","rxn","scaffolding","schema-migrations","work-in-progress"],"created_at":"2025-03-04T14:42:53.747Z","updated_at":"2025-10-26T19:42:22.032Z","avatar_url":"https://github.com/davidwyly.png","language":"PHP","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003ca href=\"https://codeclimate.com/github/davidwyly/rxn\"\u003e\u003cimg src=\"https://codeclimate.com/github/davidwyly/rxn/badges/gpa.svg\" /\u003e\u003c/a\u003e\n\u003ca href=\"https://codeclimate.com/github/davidwyly/rxn/coverage\"\u003e\u003cimg src=\"https://codeclimate.com/github/davidwyly/rxn/badges/coverage.svg\" /\u003e\u003c/a\u003e\n\u003ca href=\"https://codeclimate.com/github/davidwyly/rxn\"\u003e\u003cimg src=\"https://codeclimate.com/github/davidwyly/rxn/badges/issue_count.svg\" /\u003e\u003c/a\u003e\n\n![alt tag](http://i.imgur.com/nu63B1J.png?1)\n\n#### A fast, simple, and powerful API framework for PHP. Responds to API requests with JSON, ensuring that your backend is completely separated from your frontend\n\n##### Please note: Rxn is currently under active development and is still considered *early* alpha\n\nRxn (the abbreviation for 'reaction') is a framework designed to cut out the complexity and clutter of PHP-generated views -- offloading views to whatever frontend that suits your fancy.\n\nThe philosophy behind Rxn is simple: **strict backend / frontend decoupling**.\n\n1. The **backend** should *only* be accessible via API\n2. The **backend** should *only* render responses as JSON\n3. The **frontend** should be responsible for interpreting JSON responses\n4. The **frontend** should be responsible for generating user views\n5. Through strict **backend / frontend decoupling**, amazing things can happen\n  *  Both the **backend** and **frontend** *can be developed separately* using versioned API contracts as reference\n  *  Both the **backend** and **frontend** *have less entangling complexity*, providing a simple and clean workflow\n  *  Either the **backend** or **frontend** *can be swapped out entirely* with a completely different solution, giving you greater flexibility further down the road\n  *  Parallel **frontend** development *becomes grossly simplified* (e.g., leveraging an internal app, external app, phone app, etc using the same **backend**).\n\n## Features In Alpha\n\nIncluding planned features for beta (unchecked):\n- [X] 3.5+ GPA code climate\n- [ ] 80%+ unit test code coverage\n- [X] Gentle learning curve *(you don't have to be a guru to get up and running)*\n   - [X] Installation through Composer\n- [X] Simple workflow with an existing database schema\n   - [ ] Code generation\n      - [ ] Command Line Interface (CLI) utility to create controllers and models\n- [X] Database abstraction\n   - [X] PDO for multiple database support\n   - [X] Support for multiple database connections\n- [X] Security\n   - [X] Prepared statements *(aids against SQL injection attacks)*\n   - [ ] I/O sanitization  *(aids against XSS attacks)*\n   - [ ] Session synchronizer tokens *(aids against CSRF attacks)* \n   - [ ] SSL/TLS support *(aids against man-in-the-middle attacks)*\n- [X] Robust error handling *(throw an exception anywhere and Rxn handles the rest)*\n- [X] Debugging utilities\n- [X] Versioning *(saving you API maintenance hassles down the road)*\n   - [X] Versioned Controllers\n   - [X] Versioned Actions\n- [X] Scaffolding *(fast application prototyping)*\n   - [X] Version-less CRUD endpoints that reflect current backend records\n- [X] URI Routing\n   - [X] using Apache2\n   - [X] using NGINX\n- [X] Dependency Injection (DI) container container\n   - [X] Controller method injection\n   - [X] DI autowiring *(constructor parameters automatically injected using type-hinting)*\n- [X] Object Relational Mapping (ORM)\n   - [X] Rxn-ORM\n      - [X] CRUD operations on any database record or relation table\n      - [X] ORM autowiring *(relationships automatically derived from database structure and foreign keys)*\n      - [ ] Soft deletes\n   - [ ] Support for third-party ORMs\n- [X] Speed and Performance\n   - [X] PSR-4 Autoloading *(small footprint)*\n   - [X] Caching mechanisms\n       - [X] Native query caching *(with expiration)*\n       - [X] Object file caching *(blazing fast instantiation)*\n- [ ] Authentication  \n   - [ ] Support for third-party libraries\n     - [ ] OAUTH2\n     - [ ] OpenId\n     - [ ] SAML \n- [ ] Automated validation of API requests using existing (or generated) API contracts\n- [ ] Event logging\n- [ ] Mailer\n- [ ] Scheduler\n- [ ] Optional, modular plug-ins for loose coupling and greater flexibility\n\n## License\n\nRxn is released under the permissive and free [MIT](http://flightphp.com/license) license.\n\n## Hierarchical Namespacing and Autoloading\n\nRxn uses a namespacing structure that explicitly matches the directory structure of the class files. While also convenient, it is mainly used to implement some pretty cool autoloading features.\n\nSay, for example, that you created a class named `\\Organization\\Product\\Model\\MyAwesomeModel`. Just put the file in a directory structure that follows the namespace convention (e.g., `{root}/organization/product/model/MyAwesomeModel.php`). When you need to call the class, just invoke the class by calling it directly. There's no need to put a `require` anywhere.\n\n**BEFORE (not using autoloading):**\n```php\n\u003c?php\n\nrequire_once('/organization/product/model/MyAwesomeModel.php');\n\nuse \\Organization\\Product\\Model\\MyAwesomeModel;\n\n$object = new MyAwesomeModel()\n// object gets created!\n```\n\n**AFTER (using autoloading):**\n```php\n\u003c?php\n\nuse \\Organization\\Product\\Model\\MyAwesomeModel;\n\n$object = new MyAwesomeModel()\n// object gets created!\n```\n\nThe same pattern exists for Rxn's native classes. For example, the response class (`\\Rxn\\Framework\\Http\\Response`) is found in the `{root}/rxn/api/controller` directory. Autoloading is one of the many ways in which Rxn reduces overhead.\n\nThe following file extensions are supported by the autoloading feature (you may also define custom extensions in `\\Rxn\\Framework\\Config`):\n\n* .php\n* .class.php\n* .interface.php\n* .model.php\n* .controller.php\n\n\n## Error Handling\nRxn lives, breathes, and eats exceptions. Consider the following code snippet:\n```php\ntry {\n    $result = $databse-\u003equery($sql,$bindings);\n} catch (\\PDOException $exception) {\n    throw new \\Exception(\"Something went terribly wrong!\",422);\n}\n```\nIf you throw an `\\Exception` anywhere in the application, Rxn will self-terminate, roll back any in-process database transactions, and then gracefully respond using JSON:\n\n```javascript\n{\n    \"_rxn\": {\n        \"success\": false,\n        \"code\": 422,\n        \"result\": \"Unprocessable Entity\",\n        \"message\": \"Something went terribly wrong!\",\n        //...\n    }\n}\n```\n\n## Routing Request Parameters\n\nAn example API endpoint for your backend with Rxn might look like this:\n\n```\nhttps://yourapp.tld/v2.1/order/doSomething\n```\n\nWhere:\n\n1. `v2.1` is the endpoint `version`\n2. `order` is the `controller`\n3. `doSomething` is the controller's `action` (a public method)\n\nNow if you wanted to add a GET key-value pair to the request where `id`=`1234`, in PHP you would normally do this:\n\n**BEFORE:**\n```\nhttps://yourapp.tld/v2.1/order/someAction?id=1234\n````\nIn Rxn, you can simplify this by putting the key and value in the URL using the forward slash (`/`) as the separator, like so:\n\n**AFTER:**\n```\nhttps://yourapp.tld/v2.1/order/someAction/id/1234\n```\nAn *odd* number of parameters after the `version`, `controller`, and `action` would result in an error.\n\n## Versioned Controllers \u0026 Actions\nBy versioning your endpoint URLs (e.g., `v1.1`, `v2.4`, etc), you can rest easy knowing that you're not going to accidentally break your frontend whenever you alter backend endpoint behavior. Additionally, versioning also helps keep your documentation in order; frontend developers can just build to the documentation and everything will *just work*.\n\nSo for an endpoint with version `v2.1`, the first number (`2`) is the *controller version*, and the second number (`1`) is the *action version*. The example below is how we would declare controller version `2` with action version `1`:\n\n```php\nnamespace Organization\\Product\\Controller\\v2;\n\nclass Order extends \\Rxn\\Framework\\Http\\Controller\n{\n    public function doSomething_v1() {\n        //...\n    }\n}\n```\n This allows for maintainable, true-to-reality documentation that both frontend and backend developers can get behind.\n\n## Scaffolding\n\nWant to experiment and explore with your newfangled backend architecture? No problem, as long as you have a database schema, you have a suite of scaffolding APIs to toy with! Scaffolding endpoints are accessed using URIs that are similar to the following (note the `api` instead of the version number):\n```\nhttps://yourapp.tld/api/order/create\nhttps://yourapp.tld/api/order/read/id/{id}\nhttps://yourapp.tld/api/order/update/id/{id}\nhttps://yourapp.tld/api/order/delete/id/{id}\nhttps://yourapp.tld/api/order/search\n```\nScaffolding APIs are version-less APIs, and are designed to allow frontend developers full access to the backend in the form of Create, Read, Update, and Delete (CRUD) operations and searches. Their main benefit is that you don't have to spend a ton of time manually crafting CRUD endpoints during the early phases of application development. (As it is these early phases of development when requirements are changing, and things are constantly in flux.)\n\n**Warning:** Because Scaffolding APIs are version-less, they inheret all the problems associated with version-less APIs. As soon as the backend is altered, these APIs are altered as well; this can potentially break an application in unexpected or hidden ways. For this reason, it is wise to transition versionless APIs to versioned APIs as the development process nears completion.\n\n## Dependency Injection (DI) Container Container\nWhile most people practice some form of dependency injection without even thinking about it, the fact is, manually instantiating and injecting classes with a lot of dependencies can be a pretty big hassle. The following examples should help demonstrate the benefit of automatic dependency injection via the container container.\n\n**BEFORE (manual DI):**\n```php\n// instantiate the dependencies\n$config    = new Config();\n$database  = new Database($config);\n$registry  = new Registry($config,$database);\n$filecache = new Filecache($config);\n$map       = new Map($registry,$database,$filecache);\n\n// call the action method\n$this-\u003edoSomething_v1($registry,$database,$map);\n\npublic function doSomething_v1(Registry $registry, Database $database, Map $map) {\n    $customer = new Customer($registry,$database,$map);\n    //...\n}\n```\n\n**AFTER (using the DI container container):**\n```php\n// call the action method\n$this-\u003edoSomething_v1($app-\u003econtainer);\n\npublic function doSomething_v1(Container $container) {\n    $customer = $container-\u003eget(Customer::class);\n    //...\n}\n```\nHopefully you can see the benefits. With Rxn, there's no need to instantiate the prerequisites every time! Use the container container to make your life easier.\n\n## Controller Method Injection\nJust typehint the class you need as a parameter, and *poof*, the DI container container will guess all of the dependencies for you and automatically load and inject them. No messy requires. *You don't have to inject the dependencies manually!*\n\n**BEFORE (manual instantiation):**\n```php\n// require the dependencies\nrequire_once('/path/to/Config.php');\nrequire_once('/path/to/Collector.php');\nrequire_once('/path/to/Request.php');\n\npublic function doSomething_v1() {\n    // instantiate the dependencies\n    $config = new Config();\n    $collector = new Collector($config);\n    $request = new Request($collector,$config);\n    \n    // grab the id from the request\n    $id = $request-\u003ecollectFromGet('id');\n    //...\n}\n```\n**AFTER (automatic instantiation and injection):**\n```php\npublic function doSomething_v1(Request $request) {\n    // grab the id from the request\n    $id = $request-\u003ecollectFromGet('id');\n    //...\n}\n```\nSee the difference?\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdavidwyly%2Frxn","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdavidwyly%2Frxn","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdavidwyly%2Frxn/lists"}