{"id":2469,"date":"2024-05-15T18:23:26","date_gmt":"2024-05-15T16:23:26","guid":{"rendered":"https:\/\/nubisoft.io\/blog\/?p=2469"},"modified":"2024-06-10T16:46:50","modified_gmt":"2024-06-10T14:46:50","slug":"10x-faster-serp-after-moving-from-synchronous-to-reactive-approach","status":"publish","type":"post","link":"https:\/\/nubisoft.io\/blog\/10x-faster-serp-after-moving-from-synchronous-to-reactive-approach\/","title":{"rendered":"10x Faster SERP after moving to reactive approach"},"content":{"rendered":"<div class=\"lazyblock-text-Z2io7MR wp-block-lazyblock-text\">\n  <div style=\"--size-margin-text-block: 50px;\" class=\"lazyblocks-text-wrapper wp-block-lazyblock-text\">\n    <div class=\"lazyblocks-text-columns-wrapper\">\n              <div class=\"lazyblocks-text-column\n                  lazyblocks-text-column-border\n                \" style=\"--size-padding-text-block: 50px;\">\n          <h2>Introduction<\/h2>\n<p>Maintaining and developing an advanced IT system is a process filled with challenges. A key one is ensuring consistently high-quality services that keep pace with the dynamically changing needs of our system and customers. Goals and priorities set at the beginning evolve as our clients&#8217; businesses develop, continually stimulating the growth of our system&#8217;s functionality and the modernization of existing solutions.<\/p>        <\/div>\n      \n      \n    <\/div>\n  <\/div>\n<\/div>\n\n<div class=\"lazyblock-text-JXAs4 wp-block-lazyblock-text\">\n  <div style=\"--size-margin-text-block: 50px;\" class=\"lazyblocks-text-wrapper wp-block-lazyblock-text\">\n    <div class=\"lazyblocks-text-columns-wrapper\">\n              <div class=\"lazyblocks-text-column\n                          lazyblocks-text-big-font-quote\n        \" style=\"--size-padding-text-block: 50px;\">\n          <h2>Initial priorities<\/h2>\n<p>Campstar is a platform that enables the reservation of campers from a variety of providers &#8211; you can read about it more in a dedicated <a href=\"https:\/\/nubisoft.io\/blog\/campstar-the-travel-startup-powered-by-nubisoft\/\">Case Study<\/a>. From a technical point of view it means integration with numerous external APIs.<\/p>\n<blockquote>\n<p>The start of our collaboration was defined by two key challenges: integration with the leading European suppliers and a rapid market entry.<\/p>\n<\/blockquote>\n<p>These business priorities inspired us to create a thoughtful domain model and an advanced search engine, designed with future modifications in mind. In the initial implementation, we used a synchronous mechanism for retrieving data from suppliers via dedicated APIs.<\/p>\n<p>This approach, although relatively simple to implement, provided an adequate level of efficiency due to its low complexity and the limited number of suppliers at the initial stage, resulting in acceptable waiting times for search results.<\/p>\n<p>Simplify base model for APIConnector:<\/p>        <\/div>\n      \n      \n    <\/div>\n  <\/div>\n<\/div>\n\n\n<pre style=\"max-width:700px;\" class=\"wp-block-code\"><code>interface ApiConnector {\n \n   fun fetchAvailabilities(\n       city: City,\n       dropOffCity: City?,\n       startDateTime: ZonedDateTime,\n       endDateTime: ZonedDateTime,\n       searchAreaRadius: Float?\n   ): Collection&lt;OfferDraft&gt;\n}\n<\/code><\/pre>\n\n\n<div class=\"lazyblock-text-Z1OL3Ho wp-block-lazyblock-text\">\n  <div style=\"--size-margin-text-block: 0px;\" class=\"lazyblocks-text-wrapper wp-block-lazyblock-text\">\n    <div class=\"lazyblocks-text-columns-wrapper\">\n              <div class=\"lazyblocks-text-column\n                \" style=\"--size-padding-text-block: 5px;\">\n          <p>The simplified search model involved synchronously calling each API through the ApiConnector, then returning a collection containing the complete initial results.<\/p>\n<blockquote>\n<p>Further processing of the results is a topic for another analysis, but we will focus now on the mechanism of their retrieval.<\/p>\n<\/blockquote>\n<p>Simplify ApiConnectorRegistry and fetchAvailabilities:<\/p>        <\/div>\n      \n      \n    <\/div>\n  <\/div>\n<\/div>\n\n\n<pre style=\"max-width:700px;\" class=\"wp-block-code\"><code>@Component\nclass ApiConnectorRegistry(\n   @Autowired val connectors: Collection&lt;ApiConnector&gt;\n ) {\n\n\nfun fetchAvailabilities(\n   city: City,\n   dropOffCity: City?,\n   startDateTime: ZonedDateTime,\n   endDateTime: ZonedDateTime,\n   supplierBrand: SupplierBrand?,\n   searchAreaRadius: Float?\n): Collection&lt;OfferDraft&gt; {\nval results =  connectors\n       .flatMap { connector -&gt;\n               connector.fetchAvailabilities(city, dropOffCity, startDateTime, endDateTime, searchAreaRadius)\n           }\n   return results\n}\n<\/code><\/pre>\n\n\n<div class=\"lazyblock-text-Z1vcxdm wp-block-lazyblock-text\">\n  <div style=\"--size-margin-text-block: 50px;\" class=\"lazyblocks-text-wrapper wp-block-lazyblock-text\">\n    <div class=\"lazyblocks-text-columns-wrapper\">\n              <div class=\"lazyblocks-text-column\n                \" style=\"--size-padding-text-block: 50px;\">\n          <h2>Growing business<\/h2>\n<p>Although this model is intuitive and easy to implement, it generates significant delays. The total data retrieval time is the sum of the times to retrieve from each API.<\/p>        <\/div>\n      \n      \n    <\/div>\n  <\/div>\n<\/div>\n\n\n<figure class=\"wp-block-image size-full is-resized\"><img loading=\"lazy\" decoding=\"async\" width=\"541\" height=\"331\" src=\"https:\/\/nubisoft.io\/blog\/wp-content\/uploads\/2024\/05\/Sync.png\" alt=\"An API connector registry receives a request and then sends sequential requests to three APIs (First, Second, Third). After each request is sent, the API connector receives a corresponding response from the API.\" class=\"wp-image-2470\" style=\"width:700px\" srcset=\"https:\/\/nubisoft.io\/blog\/wp-content\/uploads\/2024\/05\/Sync.png 541w, https:\/\/nubisoft.io\/blog\/wp-content\/uploads\/2024\/05\/Sync-300x184.png 300w\" sizes=\"auto, (max-width: 541px) 100vw, 541px\" \/><\/figure>\n\n\n<div class=\"lazyblock-text-Z8njOg wp-block-lazyblock-text\">\n  <div style=\"--size-margin-text-block: 0px;\" class=\"lazyblocks-text-wrapper wp-block-lazyblock-text\">\n    <div class=\"lazyblocks-text-columns-wrapper\">\n              <div class=\"lazyblocks-text-column\n                          lazyblocks-text-big-font-quote\n        \" style=\"--size-padding-text-block: 0px;\">\n          <p>Growing business requirements led us to expand the existing infrastructure and implement new suppliers, which translated into longer waiting times for results.<\/p>\n<p>In response, we conducted an optimization of the search engine, aiming to shorten the loading time. This goal was achieved by asynchronously invoking selected APIs, without needing to modify the code of individual ApiConnector interface implementations.<\/p>\n<blockquote>\n<p>The key change was the adaptation of the fetchAvailabilities method, which began using the asynchronous CompletableFuture mechanism.<\/p>\n<\/blockquote>        <\/div>\n      \n      \n    <\/div>\n  <\/div>\n<\/div>\n\n\n<pre style=\"max-width:700px;\" class=\"wp-block-code\"><code>fun fetchAvailabilities(\n   city: City,\n   dropOffCity: City?,\n   startDateTime: ZonedDateTime,\n   endDateTime: ZonedDateTime,\n   supplierBrand: SupplierBrand?,\n   searchAreaRadius: Float?\n): Collection&lt;OfferDraft&gt; {\nval futures = connectors\n   .map { connector -&gt;\n       CompletableFuture.supplyAsync {\n           connector.fetchAvailabilities(city, dropOffCity, startDateTime, endDateTime, searchAreaRadius)                    \n   }.toTypedArray()\nCompletableFuture.allOf(*futures).join()\nreturn futures.flatMap { it.get() }\n}\n<\/code><\/pre>\n\n\n<div class=\"lazyblock-text-Z18uotq wp-block-lazyblock-text\">\n  <div style=\"--size-margin-text-block: 0px;\" class=\"lazyblocks-text-wrapper wp-block-lazyblock-text\">\n    <div class=\"lazyblocks-text-columns-wrapper\">\n              <div class=\"lazyblocks-text-column\n                          lazyblocks-text-big-font-quote\n        \" style=\"--size-padding-text-block: 0px;\">\n          <p>Thanks to this solution, asynchronous invocation of the requested APIs allowed for returning results in the time equal to the response from the slowest supplier, without requiring a complicated system refactor.<\/p>\n<blockquote>\n<p>This facilitated a smooth implementation and deployment of improvements.<\/p>\n<\/blockquote>        <\/div>\n      \n      \n    <\/div>\n  <\/div>\n<\/div>\n\n\n<figure class=\"wp-block-image size-full is-resized\"><img loading=\"lazy\" decoding=\"async\" width=\"541\" height=\"231\" src=\"https:\/\/nubisoft.io\/blog\/wp-content\/uploads\/2024\/05\/Async.png\" alt=\"The API Connector Registry receives a request and then sends sequential requests to three APIs (First, Second, and Third). The registry waits for each API to complete its operation and return a response before sending the next request.\" class=\"wp-image-2471\" style=\"width:700px\" srcset=\"https:\/\/nubisoft.io\/blog\/wp-content\/uploads\/2024\/05\/Async.png 541w, https:\/\/nubisoft.io\/blog\/wp-content\/uploads\/2024\/05\/Async-300x128.png 300w\" sizes=\"auto, (max-width: 541px) 100vw, 541px\" \/><\/figure>\n\n\n<div class=\"lazyblock-text-8fwT6 wp-block-lazyblock-text\">\n  <div style=\"--size-margin-text-block: 50px;\" class=\"lazyblocks-text-wrapper wp-block-lazyblock-text\">\n    <div class=\"lazyblocks-text-columns-wrapper\">\n              <div class=\"lazyblocks-text-column\n                          lazyblocks-text-big-font-quote\n        \" style=\"--size-padding-text-block: 50px;\">\n          <h2>Can we do better?<\/h2>\n<p>However, the main drawback of this solution was still having to wait for the response from the slowest supplier. The system&#8217;s evolution also resulted in an increase in the number of operations needed to transform the initial offer into the final one (e.g., generating insurance according to specified criteria).<\/p>\n<p>The increasing number of APIs from which data was retrieved motivated us to propose an <strong>even faster solution<\/strong> to the customer, which speeds up the generation of SERP pages and makes the processing of offers independent of the slowest supplier.<\/p>\n<p>Modified ApiConnector interface:<\/p>        <\/div>\n      \n      \n    <\/div>\n  <\/div>\n<\/div>\n\n\n<pre style=\"max-width:700px;\" class=\"wp-block-code\"><code>interface ApiConnector {\n \n   fun fetchAvailabilities(\n       city: City,\n       dropOffCity: City?,\n       startDateTime: ZonedDateTime,\n       endDateTime: ZonedDateTime,\n       searchAreaRadius: Float?\n   ): ParallelFlux &lt;OfferDraft&gt;\n}\n<\/code><\/pre>\n\n\n<div class=\"lazyblock-text-FRiEJ wp-block-lazyblock-text\">\n  <div style=\"--size-margin-text-block: 0px;\" class=\"lazyblocks-text-wrapper wp-block-lazyblock-text\">\n    <div class=\"lazyblocks-text-columns-wrapper\">\n              <div class=\"lazyblocks-text-column\n                \" style=\"--size-padding-text-block: 0px;\">\n          <p>Our solution was the refactor of both the search engine and the individual implementations of the <em>ApiConnector<\/em> interface using reactive programming and the <em>Reactor.io<\/em> library.<\/p>\n<p>After the refactor, instead of a traditional collection of results, each implementation of the <em>ApiConnector <\/em>interface now returns a <em>ParallelFlux<\/em>. The <em>fetchAvailabilities<\/em> method in the <em>ApiConnectorRegistry<\/em> class has been modified by replacing asynchronous processing with the use of reactive streams from the reactor library.<\/p>        <\/div>\n      \n      \n    <\/div>\n  <\/div>\n<\/div>\n\n\n<pre style=\"max-width:700px;\" class=\"wp-block-code\"><code>fun fetchAvailabilities(\n   city: City,\n   dropOffCity: City?,\n   startDateTime: ZonedDateTime,\n   endDateTime: ZonedDateTime,\n   supplierBrand: SupplierBrand?,\n   searchAreaRadius: Float?,\n): ParallelFlux&lt;OfferDraft&gt; {\n   return Flux.fromIterable(connectors)\n       .parallel()\n       .runOn(Schedulers.boundedElastic())\n       .flatMap { connector -&gt;\n           connector.fetchAvailabilities(\n               city,\n               dropOffCity,\n               startDateTime,\n               endDateTime,\n               searchAreaRadius\n           )\n       }\n}<\/code><\/pre>\n\n\n\n<figure class=\"wp-block-image size-full is-resized\"><img loading=\"lazy\" decoding=\"async\" width=\"541\" height=\"231\" src=\"https:\/\/nubisoft.io\/blog\/wp-content\/uploads\/2024\/05\/Reactive.png\" alt=\"The API Connector Registry receives a request and then sends a request to the First API, followed by the Second API, and finally the Third API. After each request is processed, the respective API sends a response back to the API Connector Registry.\" class=\"wp-image-2472\" style=\"width:700px\" srcset=\"https:\/\/nubisoft.io\/blog\/wp-content\/uploads\/2024\/05\/Reactive.png 541w, https:\/\/nubisoft.io\/blog\/wp-content\/uploads\/2024\/05\/Reactive-300x128.png 300w\" sizes=\"auto, (max-width: 541px) 100vw, 541px\" \/><\/figure>\n\n\n<div class=\"lazyblock-text-Z4png5 wp-block-lazyblock-text\">\n  <div style=\"--size-margin-text-block: 0px;\" class=\"lazyblocks-text-wrapper wp-block-lazyblock-text\">\n    <div class=\"lazyblocks-text-columns-wrapper\">\n              <div class=\"lazyblocks-text-column\n                          lazyblocks-text-big-font-quote\n        \" style=\"--size-padding-text-block: 0px;\">\n          <p>The <em>ApiConnectorRegistry<\/em> component is now responsible for parallel retrieval of information from various suppliers. Retrieved data in the form of <em>OfferDrafts<\/em> are forwarded for further processing until the final offer is obtained, which is returned to the end customer as one of the search results.<\/p>\n<blockquote>\n<p>Thanks to the new approach, the first results are visible to the customer in less than a second.<\/p>\n<\/blockquote>        <\/div>\n      \n      \n    <\/div>\n  <\/div>\n<\/div>\n\n\n<figure class=\"wp-block-image size-full is-resized\"><img loading=\"lazy\" decoding=\"async\" width=\"733\" height=\"301\" src=\"https:\/\/nubisoft.io\/blog\/wp-content\/uploads\/2024\/05\/Stream.png\" alt=\"The image shows a multi-step process with three identical stages. Each stage begins with two &quot;OfferDraft&quot; circles, followed by a &quot;Processing to Offer&quot; square, leading to a single &quot;Offer&quot; circle at the end of the final stage.\" class=\"wp-image-2473\" style=\"width:700px\" srcset=\"https:\/\/nubisoft.io\/blog\/wp-content\/uploads\/2024\/05\/Stream.png 733w, https:\/\/nubisoft.io\/blog\/wp-content\/uploads\/2024\/05\/Stream-300x123.png 300w\" sizes=\"auto, (max-width: 733px) 100vw, 733px\" \/><\/figure>\n\n\n<div class=\"lazyblock-text-ZcQHXc wp-block-lazyblock-text\">\n  <div style=\"--size-margin-text-block: 50px;\" class=\"lazyblocks-text-wrapper wp-block-lazyblock-text\">\n    <div class=\"lazyblocks-text-columns-wrapper\">\n              <div class=\"lazyblocks-text-column\n                  lazyblocks-text-column-border\n                \" style=\"--size-padding-text-block: 50px;\">\n          <h2>Summary<\/h2>\n<p>The evolution of our search engine is certainly not complete and will continue to be adapted to the growing needs of our customers. Throughout this process, which allowed us to accelerate data retrieval and processing by 10x, one of the priorities was the continuity of the system&#8217;s operation.<\/p>        <\/div>\n      \n      \n    <\/div>\n  <\/div>\n<\/div>\n\n<div class=\"lazyblock-posts-23lHxi wp-block-lazyblock-posts\">\n\n\n<div style=\"--size-margin-posts-block: 70px;\" class=\"lazyblocks-posts-wrapper wp-block-lazyblock-posts\">\n                                <h3 class=\"lazyblocks-posts-header\">People who read this article also read:<\/h3>                     \n\n        <div class=\"lazyblocks-posts-container\">\n\n                            <div class=\"lazyblocks-posts-single-wrapper\">\n                                        <a class=\"lazyblocks-posts-image-wrapper\" style=\"--post-thumbail-url: url('https:\/\/nubisoft.io\/blog\/wp-content\/uploads\/2024\/04\/Blog-template-4-150x150.png');\" href=\"https:\/\/nubisoft.io\/blog\/campstar-the-travel-startup-powered-by-nubisoft\/\">\n\n\n                                                    <img loading=\"lazy\" decoding=\"async\" width=\"580\" height=\"287\" src=\"https:\/\/nubisoft.io\/blog\/wp-content\/uploads\/2024\/04\/Blog-template-4-1024x507.png\" class=\"lazyblocks-posts-image wp-post-image\" alt=\"The image is of a black background with a gray Campstar logo and text stating &quot;case study&quot; at the top left. Below the text is a brown stick with two arrows, one green and one blue, pointing in opposite directions.\" srcset=\"https:\/\/nubisoft.io\/blog\/wp-content\/uploads\/2024\/04\/Blog-template-4-1024x507.png 1024w, https:\/\/nubisoft.io\/blog\/wp-content\/uploads\/2024\/04\/Blog-template-4-300x149.png 300w, https:\/\/nubisoft.io\/blog\/wp-content\/uploads\/2024\/04\/Blog-template-4-768x380.png 768w, https:\/\/nubisoft.io\/blog\/wp-content\/uploads\/2024\/04\/Blog-template-4-1200x594.png 1200w, https:\/\/nubisoft.io\/blog\/wp-content\/uploads\/2024\/04\/Blog-template-4.png 1248w\" sizes=\"auto, (max-width: 580px) 100vw, 580px\" \/>                                            <\/a>\n\n                    <div class=\"lazyblocks-posts-info-wrapper\">\n                                                    <div class=\"lazyblocks-posts-category-wrapper\">\n                                <a href=\"https:\/\/nubisoft.io\/blog\/category\/case-study\/\" rel=\"category tag\">Case study<\/a> <a href=\"https:\/\/nubisoft.io\/blog\/category\/e-commerce\/\" rel=\"category tag\">E-commerce<\/a>                            <\/div>\n                        \n                        <span class=\"lazyblocks-posts-read-time\">1 min read<\/span>                    <\/div>\n\n\n                    <div class=\"lazyblocks-posts-title-wrapper\">\n                        <h2 class=\"lazyblocks-posts-title\"><a href=\"https:\/\/nubisoft.io\/blog\/campstar-the-travel-startup-powered-by-nubisoft\/\">Campstar: The Travel Startup Powered by NubiSoft<\/a><\/h2>\n                        <a class=\"lazyblocks-posts-read-more\" href=\"https:\/\/nubisoft.io\/blog\/campstar-the-travel-startup-powered-by-nubisoft\/\">Read more<\/a>\n                    <\/div>\n                <\/div>\n                            <div class=\"lazyblocks-posts-single-wrapper\">\n                                        <a class=\"lazyblocks-posts-image-wrapper\" style=\"--post-thumbail-url: url('https:\/\/nubisoft.io\/blog\/wp-content\/uploads\/2024\/01\/Blog-templateWspolpraca-150x150.png');\" href=\"https:\/\/nubisoft.io\/blog\/4-ways-to-stand-out-in-the-e-commerce-market\/\">\n\n\n                                                    <img loading=\"lazy\" decoding=\"async\" width=\"580\" height=\"287\" src=\"https:\/\/nubisoft.io\/blog\/wp-content\/uploads\/2024\/01\/Blog-templateWspolpraca-1024x507.png\" class=\"lazyblocks-posts-image wp-post-image\" alt=\"\" srcset=\"https:\/\/nubisoft.io\/blog\/wp-content\/uploads\/2024\/01\/Blog-templateWspolpraca-1024x507.png 1024w, https:\/\/nubisoft.io\/blog\/wp-content\/uploads\/2024\/01\/Blog-templateWspolpraca-300x149.png 300w, https:\/\/nubisoft.io\/blog\/wp-content\/uploads\/2024\/01\/Blog-templateWspolpraca-768x380.png 768w, https:\/\/nubisoft.io\/blog\/wp-content\/uploads\/2024\/01\/Blog-templateWspolpraca-1200x594.png 1200w, https:\/\/nubisoft.io\/blog\/wp-content\/uploads\/2024\/01\/Blog-templateWspolpraca.png 1248w\" sizes=\"auto, (max-width: 580px) 100vw, 580px\" \/>                                            <\/a>\n\n                    <div class=\"lazyblocks-posts-info-wrapper\">\n                                                    <div class=\"lazyblocks-posts-category-wrapper\">\n                                <a href=\"https:\/\/nubisoft.io\/blog\/category\/e-commerce\/\" rel=\"category tag\">E-commerce<\/a>                            <\/div>\n                        \n                        <span class=\"lazyblocks-posts-read-time\">3 min read<\/span>                    <\/div>\n\n\n                    <div class=\"lazyblocks-posts-title-wrapper\">\n                        <h2 class=\"lazyblocks-posts-title\"><a href=\"https:\/\/nubisoft.io\/blog\/4-ways-to-stand-out-in-the-e-commerce-market\/\">4 Ways to Stand Out in the E-commerce Market<\/a><\/h2>\n                        <a class=\"lazyblocks-posts-read-more\" href=\"https:\/\/nubisoft.io\/blog\/4-ways-to-stand-out-in-the-e-commerce-market\/\">Read more<\/a>\n                    <\/div>\n                <\/div>\n                            <div class=\"lazyblocks-posts-single-wrapper\">\n                                        <a class=\"lazyblocks-posts-image-wrapper\" style=\"--post-thumbail-url: url('https:\/\/nubisoft.io\/blog\/wp-content\/uploads\/2024\/01\/Blog-template-150x150.png');\" href=\"https:\/\/nubisoft.io\/blog\/the-top-3-end-to-end-testing-tools\/\">\n\n\n                                                    <img loading=\"lazy\" decoding=\"async\" width=\"580\" height=\"287\" src=\"https:\/\/nubisoft.io\/blog\/wp-content\/uploads\/2024\/01\/Blog-template-1024x507.png\" class=\"lazyblocks-posts-image wp-post-image\" alt=\"\" srcset=\"https:\/\/nubisoft.io\/blog\/wp-content\/uploads\/2024\/01\/Blog-template-1024x507.png 1024w, https:\/\/nubisoft.io\/blog\/wp-content\/uploads\/2024\/01\/Blog-template-300x149.png 300w, https:\/\/nubisoft.io\/blog\/wp-content\/uploads\/2024\/01\/Blog-template-768x380.png 768w, https:\/\/nubisoft.io\/blog\/wp-content\/uploads\/2024\/01\/Blog-template-1200x594.png 1200w, https:\/\/nubisoft.io\/blog\/wp-content\/uploads\/2024\/01\/Blog-template.png 1248w\" sizes=\"auto, (max-width: 580px) 100vw, 580px\" \/>                                            <\/a>\n\n                    <div class=\"lazyblocks-posts-info-wrapper\">\n                                                    <div class=\"lazyblocks-posts-category-wrapper\">\n                                <a href=\"https:\/\/nubisoft.io\/blog\/category\/software-development\/\" rel=\"category tag\">Software Development<\/a>                            <\/div>\n                        \n                        <span class=\"lazyblocks-posts-read-time\">13 min read<\/span>                    <\/div>\n\n\n                    <div class=\"lazyblocks-posts-title-wrapper\">\n                        <h2 class=\"lazyblocks-posts-title\"><a href=\"https:\/\/nubisoft.io\/blog\/the-top-3-end-to-end-testing-tools\/\">The Top 3 End-to-End Testing Tools<\/a><\/h2>\n                        <a class=\"lazyblocks-posts-read-more\" href=\"https:\/\/nubisoft.io\/blog\/the-top-3-end-to-end-testing-tools\/\">Read more<\/a>\n                    <\/div>\n                <\/div>\n                    <\/div>\n    <\/div>\n<\/div>","protected":false},"excerpt":{"rendered":"<p>Finding the ideal RV rental just got quicker. Learn how we turned Campstar&#8217;s search into a seamless experience thanks to a reactive approach. <\/p>\n","protected":false},"author":17,"featured_media":2523,"comment_status":"open","ping_status":"open","sticky":false,"template":"post-2024.php","format":"standard","meta":{"_case_study_excerpt":"","footnotes":""},"categories":[80,3],"tags":[517,32,514,46,39],"class_list":["post-2469","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-case-study","category-software-development","tag-api-en","tag-aws","tag-kotlin","tag-kubernetes","tag-reactive"],"_links":{"self":[{"href":"https:\/\/nubisoft.io\/blog\/wp-json\/wp\/v2\/posts\/2469","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/nubisoft.io\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/nubisoft.io\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/nubisoft.io\/blog\/wp-json\/wp\/v2\/users\/17"}],"replies":[{"embeddable":true,"href":"https:\/\/nubisoft.io\/blog\/wp-json\/wp\/v2\/comments?post=2469"}],"version-history":[{"count":13,"href":"https:\/\/nubisoft.io\/blog\/wp-json\/wp\/v2\/posts\/2469\/revisions"}],"predecessor-version":[{"id":2753,"href":"https:\/\/nubisoft.io\/blog\/wp-json\/wp\/v2\/posts\/2469\/revisions\/2753"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/nubisoft.io\/blog\/wp-json\/wp\/v2\/media\/2523"}],"wp:attachment":[{"href":"https:\/\/nubisoft.io\/blog\/wp-json\/wp\/v2\/media?parent=2469"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/nubisoft.io\/blog\/wp-json\/wp\/v2\/categories?post=2469"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/nubisoft.io\/blog\/wp-json\/wp\/v2\/tags?post=2469"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}