{"id":14677373,"url":"https://github.com/cerbero90/lazy-json-pages","last_synced_at":"2025-12-25T05:48:42.337Z","repository":{"id":62500716,"uuid":"389870204","full_name":"cerbero90/lazy-json-pages","owner":"cerbero90","description":"📜 Framework-agnostic API scraper to load items from any paginated JSON API into a Laravel lazy collection via async HTTP requests.","archived":false,"fork":false,"pushed_at":"2024-09-10T23:55:40.000Z","size":180,"stargazers_count":104,"open_issues_count":0,"forks_count":2,"subscribers_count":3,"default_branch":"develop","last_synced_at":"2024-09-11T19:59:05.087Z","etag":null,"topics":["api","json","laravel","lazy","pagination","parser","php","scraper","scraping","stream"],"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/cerbero90.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":".github/FUNDING.yml","license":"LICENSE.md","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null},"funding":{"github":"cerbero90"}},"created_at":"2021-07-27T06:18:42.000Z","updated_at":"2024-09-11T16:04:31.000Z","dependencies_parsed_at":"2024-09-09T17:30:36.213Z","dependency_job_id":null,"html_url":"https://github.com/cerbero90/lazy-json-pages","commit_stats":{"total_commits":34,"total_committers":1,"mean_commits":34.0,"dds":0.0,"last_synced_commit":"66eebd947809136d4de190cb18137f3c4dc56266"},"previous_names":[],"tags_count":1,"template":false,"template_full_name":"cerbero90/skeleton","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cerbero90%2Flazy-json-pages","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cerbero90%2Flazy-json-pages/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cerbero90%2Flazy-json-pages/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cerbero90%2Flazy-json-pages/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/cerbero90","download_url":"https://codeload.github.com/cerbero90/lazy-json-pages/tar.gz/refs/heads/develop","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":218774690,"owners_count":16385615,"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","json","laravel","lazy","pagination","parser","php","scraper","scraping","stream"],"created_at":"2024-09-12T09:01:19.298Z","updated_at":"2025-12-25T05:48:42.279Z","avatar_url":"https://github.com/cerbero90.png","language":"PHP","funding_links":["https://github.com/sponsors/cerbero90"],"categories":["PHP"],"sub_categories":[],"readme":"# 📜 Lazy JSON Pages\n\n[![Author][ico-author]][link-author]\n[![PHP Version][ico-php]][link-php]\n[![Build Status][ico-actions]][link-actions]\n[![Coverage Status][ico-scrutinizer]][link-scrutinizer]\n[![Quality Score][ico-code-quality]][link-code-quality]\n[![PHPStan Level][ico-phpstan]][link-phpstan]\n[![Latest Version][ico-version]][link-packagist]\n[![Software License][ico-license]](LICENSE.md)\n[![PER][ico-per]][link-per]\n[![Total Downloads][ico-downloads]][link-downloads]\n\n```php\nuse Illuminate\\Support\\LazyCollection;\n\nLazyCollection::fromJsonPages($source)\n    -\u003etotalPages('pagination.total_pages')\n    -\u003easync(requests: 3)\n    -\u003ethrottle(requests: 100, perMinutes: 1)\n    -\u003ecollect('data.*');\n```\n\nFramework-agnostic API scraper to load items from any paginated JSON API into a [Laravel lazy collection](https://laravel.com/docs/collections#lazy-collections) via async HTTP requests.\n\n\u003e [!TIP]\n\u003e Need to read large JSON with no pagination in a memory-efficient way?\n\u003e\n\u003e Consider using [🐼 Lazy JSON](https://github.com/cerbero90/lazy-json) or [🧩 JSON Parser](https://github.com/cerbero90/json-parser) instead.\n\n\n## 📦 Install\n\nVia Composer:\n\n``` bash\ncomposer require cerbero/lazy-json-pages\n```\n\n\n## 🔮 Usage\n\n* [👣 Basics](#-basics)\n* [💧 Sources](#-sources)\n* [🏛️ Pagination structure](#%EF%B8%8F-pagination-structure)\n* [📏 Length-aware paginations](#-length-aware-paginations)\n* [↪️ Cursor-aware paginations](#%EF%B8%8F-cursor-aware-paginations)\n* [🔗 Link header paginations](#-link-header-paginations)\n* [👽 Custom paginations](#-custom-paginations)\n* [🚀 Requests optimization](#-requests-optimization)\n* [💢 Errors handling](#-errors-handling)\n* [🤝 Laravel integration](#-laravel-integration)\n\n\n### 👣 Basics\n\nDepending on our coding style, we can instantiate Lazy JSON Pages in 4 different ways:\n\n```php\nuse Cerbero\\LazyJsonPages\\LazyJsonPages;\nuse Illuminate\\Support\\LazyCollection;\n\nuse function Cerbero\\LazyJsonPages\\lazyJsonPages;\n\n// lazy collection macro\nLazyCollection::fromJsonPages($source);\n\n// classic instantiation\nnew LazyJsonPages($source);\n\n// static method\nLazyJsonPages::from($source);\n\n// namespaced helper\nlazyJsonPages($source);\n```\n\nThe variable `$source` in our examples represents any [source](#-sources) that points to a paginated JSON API. Once we define the source, we can then chain methods to define how the API is paginated:\n\n```php\n$lazyCollection = LazyJsonPages::from($source)\n    -\u003etotalItems('pagination.total_items')\n    -\u003eoffset()\n    -\u003ecollect('results.*');\n```\n\nWhen calling `collect()`, we indicate that the pagination structure is defined and that we are ready to collect the paginated items within a [Laravel lazy collection](https://laravel.com/docs/collections#lazy-collections), where we can loop through the items one by one and apply filters and transformations in a memory-efficient way.\n\n\n### 💧 Sources\n\nA source is any means that can point to a paginated JSON API. A number of sources is supported by default:\n\n- **endpoint URIs**, e.g. `https://example.com/api/v1/users` or any instance of `Psr\\Http\\Message\\UriInterface`\n- **PSR-7 requests**, i.e. any instance of `Psr\\Http\\Message\\RequestInterface`\n- **Laravel HTTP client requests**, i.e. any instance of `Illuminate\\Http\\Client\\Request`\n- **Laravel HTTP client responses**, i.e. any instance of `Illuminate\\Http\\Client\\Response`\n- **Laravel HTTP requests**, i.e. any instance of `Illuminate\\Http\\Request`\n- **Symfony requests**, i.e. any instance of `Symfony\\Component\\HttpFoundation\\Request`\n- **user-defined sources**, i.e. any instance of `Cerbero\\LazyJsonPages\\Sources\\Source`\n\nHere are some examples of sources:\n\n```php\n// a simple URI string\n$source = 'https://example.com/api/v1/users';\n\n// any PSR-7 compatible request is supported, including Guzzle requests\n$source = new GuzzleHttp\\Psr7\\Request('GET', 'https://example.com/api/v1/users');\n\n// while being framework-agnostic, Lazy JSON Pages integrates well with Laravel\n$source = Http::withToken($bearer)-\u003eget('https://example.com/api/v1/users');\n```\n\nIf none of the above sources satifies our use case, we can implement our own source.\n\n\u003cdetails\u003e\u003csummary\u003e\u003cb\u003eClick here to see how to implement a custom source.\u003c/b\u003e\u003c/summary\u003e\n\nTo implement a custom source, we need to extend `Source` and implement 2 methods:\n\n```php\nuse Cerbero\\LazyJsonPages\\Sources\\Source;\nuse Psr\\Http\\Message\\RequestInterface;\nuse Psr\\Http\\Message\\ResponseInterface;\n\nclass CustomSource extends Source\n{\n    public function request(): RequestInterface\n    {\n        // return a PSR-7 request\n    }\n\n    public function response(): ResponseInterface\n    {\n        // return a PSR-7 response\n    }\n}\n```\n\nThe parent class `Source` gives us access to 2 properties:\n- `$source`: the custom source for our use case\n- `$client`: the Guzzle HTTP client\n\nThe methods to implement turn our custom source into a PSR-7 request and a PSR-7 response. Please refer to the [already existing sources](https://github.com/cerbero90/json-parser/tree/master/src/Sources) to see some implementations.\n\nOnce the custom source is implemented, we can instruct Lazy JSON Pages to use it:\n\n```php\nLazyJsonPages::from(new CustomSource($source));\n```\n\nIf you find yourself implementing the same custom source in different projects, feel free to send a PR and we will consider to support your custom source by default. Thank you in advance for any contribution!\n\u003c/details\u003e\n\n\n### 🏛️ Pagination structure\n\nAfter defining the [source](#-sources), we need to let Lazy JSON Pages know what the paginated API looks like.\n\nIf the API uses a query parameter different from `page` to specify the current page - for example `?current_page=1` - we can chain the method `pageName()`:\n\n```php\nLazyJsonPages::from($source)-\u003epageName('current_page');\n```\n\nOtherwise, if the number of the current page is present in the URI path - for example `https://example.com/users/page/1` - we can chain the method `pageInPath()`:\n\n```php\nLazyJsonPages::from($source)-\u003epageInPath();\n```\n\nBy default the last integer in the URI path is considered the page number. However we can customize the regular expression used to capture the page number, if need be:\n\n```php\nLazyJsonPages::from($source)-\u003epageInPath('~/page/(\\d+)$~');\n```\n\nSome API paginations may start with a page different from `1`. If that's the case, we can define the first page by chaining the method `firstPage()`:\n\n```php\nLazyJsonPages::from($source)-\u003efirstPage(0);\n```\n\nNow that we have customized the basic structure of the API, we can describe how items are paginated depending on whether the pagination is [length-aware](#-length-aware-paginations) or [cursor](#%EF%B8%8F-cursor-and-next-page-paginations) based.\n\n\n### 📏 Length-aware paginations\n\nThe term \"length-aware\" indicates any pagination containing at least one of the following length information:\n- the total number of pages\n- the total number of items\n- the number of the last page\n\nLazy JSON Pages only needs one of these details to work properly:\n\n```php\nLazyJsonPages::from($source)-\u003etotalPages('pagination.total_pages');\n\nLazyJsonPages::from($source)-\u003etotalItems('pagination.total_items');\n\nLazyJsonPages::from($source)-\u003elastPage('pagination.last_page');\n```\n\nIf the length information is nested in the JSON body, we can use dot-notation to indicate the level of nesting. For example, `pagination.total_pages` means that the total number of pages sits in the object `pagination`, under the key `total_pages`.\n\nOtherwise, if the length information is displayed in the headers, we can use the same methods to gather it by simply defining the name of the header:\n\n```php\nLazyJsonPages::from($source)-\u003etotalPages('X-Total-Pages');\n\nLazyJsonPages::from($source)-\u003etotalItems('X-Total-Items');\n\nLazyJsonPages::from($source)-\u003elastPage('X-Last-Page');\n```\n\nAPIs can expose their length information in the form of numbers (`total_pages: 10`) or URIs (`last_page: \"https://example.com?page=10\"`), Lazy JSON Pages supports both.\n\nIf the pagination works with an offset, we can configure it with the `offset()` method. The value of the offset will be calculated based on the number of items present on the first page:\n\n```php\n// indicate that the offset is defined by the `offset` query parameter, e.g. ?offset=50\nLazyJsonPages::from($source)\n    -\u003etotalItems('pagination.total_items')\n    -\u003eoffset();\n\n// indicate that the offset is defined by the `skip` query parameter, e.g. ?skip=50\nLazyJsonPages::from($source)\n    -\u003etotalItems('pagination.total_items')\n    -\u003eoffset('skip');\n```\n\n\n### ↪️ Cursor-aware paginations\n\nNot all paginations are [length-aware](#-length-aware-paginations), some may be built in a way where each page has a cursor pointing to the next page.\n\nWe can tackle this kind of pagination by indicating the key or the header holding the cursor:\n\n```php\nLazyJsonPages::from($source)-\u003ecursor('pagination.cursor');\n\nLazyJsonPages::from($source)-\u003ecursor('X-Cursor');\n```\n\nThe cursor may be a number, a string or a URI: Lazy JSON Pages supports them all.\n\n\n### 🔗 Link header paginations\n\nSome paginated API responses include a header called `Link`. An example is [GitHub](https://api.github.com/repos/octocat/hello-world/issues?state=open): if we inspect the response headers, we can see the `Link` header looking like this:\n\n```\n\u003chttps://api.github.com/repositories/1296269/issues?state=open\u0026page=2\u003e; rel=\"next\",\n\u003chttps://api.github.com/repositories/1296269/issues?state=open\u0026page=43\u003e; rel=\"last\"\n```\n\nTo lazy-load items from a Link header pagination, we can chain the method `linkHeader()`:\n\n```php\nLazyJsonPages::from($source)-\u003elinkHeader();\n```\n\n\n### 👽 Custom paginations\n\nLazy JSON Pages provides several methods to extract items from the most popular pagination mechanisms. However if we need a custom solution, we can implement our own pagination.\n\n\u003cdetails\u003e\u003csummary\u003e\u003cb\u003eClick here to see how to implement a custom pagination.\u003c/b\u003e\u003c/summary\u003e\n\nTo implement a custom pagination, we need to extend `Pagination` and implement 1 method:\n\n```php\nuse Cerbero\\LazyJsonPages\\Paginations\\Pagination;\nuse Traversable;\n\nclass CustomPagination extends Pagination\n{\n    public function getIterator(): Traversable\n    {\n        // return a Traversable yielding the paginated items\n    }\n}\n```\n\nThe parent class `Pagination` gives us access to 3 properties:\n- `$source`: the [source](#-sources) pointing to the paginated JSON API\n- `$client`: the Guzzle HTTP client\n- `$config`: the configuration that we generated by chaining methods like `totalPages()`\n\nThe method `getIterator()` defines the logic to extract paginated items in a memory-efficient way. Please refer to the [already existing paginations](https://github.com/cerbero90/json-parser/tree/master/src/Paginations) to see some implementations.\n\nOnce the custom pagination is implemented, we can instruct Lazy JSON Pages to use it:\n\n```php\nLazyJsonPages::from($source)-\u003epagination(CustomPagination::class);\n```\n\nIf you find yourself implementing the same custom pagination in different projects, feel free to send a PR and we will consider to support your custom pagination by default. Thank you in advance for any contribution!\n\u003c/details\u003e\n\n\n### 🚀 Requests optimization\n\nPaginated APIs differ from each other, so Lazy JSON Pages lets us tweak our HTTP requests specifically for our use case.\n\nBy default HTTP requests are sent synchronously. If we want to send more than one request without waiting for the response, we can call the `async()` method and set the number of concurrent requests:\n\n```php\nLazyJsonPages::from($source)-\u003easync(requests: 5);\n```\n\n\u003e [!NOTE]  \n\u003e Please note that asynchronous requests improve speed at the expense of memory, as more responses are going to be loaded at once.\n\nSeveral APIs set rate limits to reduce the number of allowed requests for a period of time. We can instruct Lazy JSON Pages to respect such limits by throttling our requests:\n\n```php\n// we send a maximum of 3 requests per second, 60 per minute and 3,000 per hour\nLazyJsonPages::from($source)\n    -\u003ethrottle(requests: 3, perSeconds: 1)\n    -\u003ethrottle(requests: 60, perMinutes: 1)\n    -\u003ethrottle(requests: 3000, perHours: 1);\n```\n\nInternally, Lazy JSON Pages uses [Guzzle](https://docs.guzzlephp.org) as its HTTP client. We can customize the client behavior by adding as many [middleware](https://docs.guzzlephp.org/en/stable/handlers-and-middleware.html#middleware) as we need:\n\n```php\nLazyJsonPages::from($source)\n    -\u003emiddleware('log_requests', $logRequests)\n    -\u003emiddleware('cache_responses', $cacheResponses);\n```\n\nIf we need a middleware to be added every time we invoke Lazy JSON Pages, we can add a global middleware:\n\n```php\nLazyJsonPages::globalMiddleware('fire_events', $fireEvents);\n```\n\nSometimes writing [Guzzle middleware](https://docs.guzzlephp.org/en/stable/handlers-and-middleware.html#middleware) might be cumbersome. Alternatively Lazy JSON Pages provides convenient methods to fire callbacks when sending a request or receiving a response:\n\n```php\nuse Psr\\Http\\Message\\RequestInterface;\nuse Psr\\Http\\Message\\ResponseInterface;\n\nLazyJsonPages::from($source)\n    -\u003eonRequest(fn(RequestInterface $request) =\u003e ...)\n    -\u003eonResponse(fn(ResponseInterface $response, RequestInterface $request) =\u003e ...);\n```\n\nWe can also tweak the number of allowed seconds before an API connection times out or the allowed duration of the entire HTTP request (by default they are both set to 5 seconds):\n\n```php\nLazyJsonPages::from($source)\n    -\u003econnectionTimeout(7)\n    -\u003erequestTimeout(10);\n```\n\nIf the 3rd party API is faulty or error-prone, we can indicate how many times we want to retry failing HTTP requests and the backoff strategy to compute the milliseconds to wait before retrying (by default failing requests are repeated 3 times after an exponential backoff of 100, 400 and 900 milliseconds):\n\n```php\n// repeat failing requests 5 times after a backoff of 1, 2, 3, 4 and 5 seconds\nLazyJsonPages::from($source)\n    -\u003eattempts(5)\n    -\u003ebackoff(fn(int $attempt) =\u003e $attempt * 1000);\n```\n\n### 💢 Errors handling\n\nIf something goes wrong during the scraping process, we can intercept the error and execute a custom logic to handle it:\n\n```php\nuse Psr\\Http\\Message\\RequestInterface;\nuse Psr\\Http\\Message\\ResponseInterface;\n\nLazyJsonPages::from($source)\n    -\u003eonError(fn(Throwable $e, RequestInterface $request, ?ResponseInterface $response) =\u003e ...);\n```\n\nAny exception thrown by this package extends the `LazyJsonPagesException` class. This makes it easy to handle all exceptions in a single catch block:\n\n```php\nuse Cerbero\\LazyJsonPages\\Exceptions\\LazyJsonPagesException;\n\ntry {\n    LazyJsonPages::from($source)-\u003elinkHeader()-\u003ecollect()-\u003eeach(...);\n} catch (LazyJsonPagesException $e) {\n    // handle any exception thrown by Lazy JSON Pages\n}\n```\n\nFor reference, here is a comprehensive table of all the exceptions thrown by this package:\n\n|`Cerbero\\LazyJsonPages\\Exceptions\\`|thrown when|\n|---|---|\n|`InvalidKeyException`|a JSON key does not contain a valid value|\n|`InvalidPageInPathException`|a page cannot be found in the URI path|\n|`InvalidPaginationException`|a pagination implementation is not valid|\n|`OutOfAttemptsException`|an HTTP request failed too many times|\n|`RequestNotSentException`|a JSON source didn't send any HTTP request|\n|`UnsupportedPaginationException`|a pagination is not supported|\n|`UnsupportedSourceException`|a JSON source is not supported|\n\n\n### 🤝 Laravel integration\n\nIf used in a [Laravel](https://laravel.com) project, Lazy JSON Pages automatically fires events when:\n- an HTTP request is about to be sent, by firing `Illuminate\\Http\\Client\\Events\\RequestSending`\n- an HTTP response is received, by firing `Illuminate\\Http\\Client\\Events\\ResponseReceived`\n- a connection failed, by firing `Illuminate\\Http\\Client\\Events\\ConnectionFailed`\n\nThis is especially handy for debugging tools like [Laravel Telescope](https://laravel.com/docs/telescope) or [Spatie Ray](https://spatie.be/docs/ray/installation-in-your-project/laravel) or for triggering the related event listeners.\n\n\n## 📆 Change log\n\nPlease see [CHANGELOG](CHANGELOG.md) for more information on what has changed recently.\n\n## 🧪 Testing\n\n``` bash\ncomposer test\n```\n\n## 💞 Contributing\n\nPlease see [CONTRIBUTING](CONTRIBUTING.md) and [CODE_OF_CONDUCT](CODE_OF_CONDUCT.md) for details.\n\n## 🧯 Security\n\nIf you discover any security related issues, please email andrea.marco.sartori@gmail.com instead of using the issue tracker.\n\n## 🏅 Credits\n\n- [Andrea Marco Sartori][link-author]\n- [All Contributors][link-contributors]\n\n## ⚖️ License\n\nThe MIT License (MIT). Please see [License File](LICENSE.md) for more information.\n\n[ico-author]: https://img.shields.io/static/v1?label=author\u0026message=cerbero90\u0026color=50ABF1\u0026logo=twitter\u0026style=flat-square\n[ico-php]: https://img.shields.io/packagist/php-v/cerbero/lazy-json-pages?color=%234F5B93\u0026logo=php\u0026style=flat-square\n[ico-version]: https://img.shields.io/packagist/v/cerbero/lazy-json-pages.svg?label=version\u0026style=flat-square\n[ico-actions]: https://img.shields.io/github/actions/workflow/status/cerbero90/lazy-json-pages/build.yml?branch=master\u0026style=flat-square\u0026logo=github\n[ico-license]: https://img.shields.io/badge/license-MIT-brightgreen.svg?style=flat-square\n[ico-per]: https://img.shields.io/static/v1?label=compliance\u0026message=PER\u0026color=blue\u0026style=flat-square\n[ico-scrutinizer]: https://img.shields.io/scrutinizer/coverage/g/cerbero90/lazy-json-pages.svg?style=flat-square\u0026logo=scrutinizer\n[ico-code-quality]: https://img.shields.io/scrutinizer/g/cerbero90/lazy-json-pages.svg?style=flat-square\u0026logo=scrutinizer\n[ico-phpstan]: https://img.shields.io/badge/level-max-success?style=flat-square\u0026logo=data:image/png;base64,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\n[ico-downloads]: https://img.shields.io/packagist/dt/cerbero/lazy-json-pages.svg?style=flat-square\n\n[link-author]: https://twitter.com/cerbero90\n[link-php]: https://www.php.net\n[link-packagist]: https://packagist.org/packages/cerbero/lazy-json-pages\n[link-actions]: https://github.com/cerbero90/lazy-json-pages/actions?query=workflow%3Abuild\n[link-per]: https://www.php-fig.org/per/coding-style/\n[link-scrutinizer]: https://scrutinizer-ci.com/g/cerbero90/lazy-json-pages/code-structure\n[link-code-quality]: https://scrutinizer-ci.com/g/cerbero90/lazy-json-pages\n[link-downloads]: https://packagist.org/packages/cerbero/lazy-json-pages\n[link-phpstan]: https://phpstan.org/\n[link-contributors]: ../../contributors\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcerbero90%2Flazy-json-pages","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcerbero90%2Flazy-json-pages","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcerbero90%2Flazy-json-pages/lists"}