{"id":18324,"date":"2019-03-13T09:12:34","date_gmt":"2019-03-13T08:12:34","guid":{"rendered":"https:\/\/blog.trifork.com\/?p=18324"},"modified":"2019-03-13T09:12:34","modified_gmt":"2019-03-13T08:12:34","slug":"retry-functionality-in-a-reactive-programming-context","status":"publish","type":"post","link":"https:\/\/trifork.nl\/blog\/retry-functionality-in-a-reactive-programming-context\/","title":{"rendered":"Retry functionality in a reactive programming  context"},"content":{"rendered":"\n<p>Reactive programming can be used to solve a lot of different use cases. For<br \/>instance, reactive programming can be really helpful for cases where timing is an issue. An example of such a case is retry logic with delay or backoff functionality.<\/p>\n\n\n\n<p> Let&#8217;s have a look at some different implementations using Project Reactor<br \/><a href=\"https:\/\/projectreactor.io\"> (https:\/\/projectreactor.io<\/a>) for Java. The sourcecode of the examples can be found at this <a href=\"https:\/\/github.com\/erwindeg\/reactor-examples\">repository<\/a>.  <\/p>\n\n\n\n<h4 class=\"wp-block-heading\">Using Reactor to retry with exponentional backoff <\/h4>\n\n\n\n<p>In the next examples we will be calling the callAPI function as shown below.  This function simulates throwing an exception while calling an API.<br \/><\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: java; title: ; notranslate\" title=\"\">\nprivate Mono callAPI() {\n    return Mono.defer(() -&gt; {\n       System.out.println(&quot;API call &quot;);\n        return api.monoWithException();\n    });\n} \n<\/pre><\/div>\n\n\n<p>In our first attempt we use the reactor operator retryWhen. This operator can be used to resubscribe to the publisher in case of an exception. As a parameter  it takes a function that allows us to conditionaly retry. The following marble  diagram (from project reactor\u2019s javadoc) describes this behavior: <\/p>\n\n\n\n<figure class=\"wp-block-image\"><img loading=\"lazy\" decoding=\"async\" width=\"862\" height=\"585\" src=\"https:\/\/trifork.nl\/articles\/wp-content\/uploads\/sites\/3\/2019\/03\/reactive-Retry-functionality-in-a-reactive-programming-context.jpg\" alt=\"\" class=\"wp-image-18325\" srcset=\"https:\/\/trifork.nl\/blog\/wp-content\/uploads\/sites\/3\/2019\/03\/reactive-Retry-functionality-in-a-reactive-programming-context.jpg 862w, https:\/\/trifork.nl\/blog\/wp-content\/uploads\/sites\/3\/2019\/03\/reactive-Retry-functionality-in-a-reactive-programming-context-300x204.jpg 300w, https:\/\/trifork.nl\/blog\/wp-content\/uploads\/sites\/3\/2019\/03\/reactive-Retry-functionality-in-a-reactive-programming-context-768x521.jpg 768w\" sizes=\"auto, (max-width: 862px) 100vw, 862px\" \/><figcaption> <br \/>Figure 1: retryWhen <\/figcaption><\/figure>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: java; title: ; notranslate\" title=\"\">\ncallAPI()\n     .retryWhen(errors -&gt; errors\n             .zipWith(Flux.range(1, 4), (n, i) -&gt; i)\n             .flatMap(error -&gt; Mono.delay(ofMillis(10))));\n<\/pre><\/div>\n\n\n<p>We combine each error with a entry from the range, using zipWith and than delay the emit using a flatMap:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: java; title: ; notranslate\" title=\"\">\nzipWith(error1,1).flatMap(delay) \/\/ resubscribes after 10ms\nzipWith(error2,2).flatMap(delay) \/\/ resubscribes after 10ms\nzipWith(error3,3).flatMap(delay) \/\/ resubscribes after 10ms\nzipWith(error4,4).flatMap(delay) \/\/ completes\n<\/pre><\/div>\n\n\n<p><br \/>After the first try and 4 retries, the producer completes when the api call keeps producing errors. In this example the delay time is constant, with a little change we can make it exponential to implement the&nbsp;<a href=\"https:\/\/en.wikipedia.org\/wiki\/Exponential_backoff\">exponential backoff<\/a> pattern.<br \/><\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: java; title: ; notranslate\" title=\"\">\ncallAPI()\n     .retryWhen(errors -&gt; errors\n           .zipWith(Flux.range(1, 4), (n, i) -&gt; i)\n           .flatMap(error -&gt; Mono.delay(ofMillis((int) Math.pow(10, error)))));\n<\/pre><\/div>\n\n\n<p>The downside of this approach is that after the retries are done and the api call keeps producing errors, the operation terminates without an error. Ideally, you want the error to be propagated to be able to handle it. <\/p>\n\n\n\n<h4 class=\"wp-block-heading\"> <br \/>Existing retry logic in reactor and reactor-extras <\/h4>\n\n\n\n<p>The reactor core library has a retryBackoff method, which takes as arguments the maximum number of retries, and initial backoff delay and a maximum backoff delay. This retry uses a randomized <a href=\"https:\/\/projectreactor.io\/docs\/core\/release\/api\/reactor\/core\/publisher\/Mono.html#retryBackoff-long-java.time.Duration-\"> exponentional<\/a><a class=\"\" href=\"https:\/\/projectreactor.io\/docs\/core\/release\/api\/reactor\/core\/publisher\/Mono.html#retryBackoff-long-java.time.Duration-\"> backoff strategy<\/a>. If&nbsp;the callAPI function still gives an error after the specified number of retries, the retryBackoff returns an IllegalStateException. This allows us to log the exception, call exception logic and\/or return a default value.<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: java; title: ; notranslate\" title=\"\">\ncallAPI().retryBackoff(5, ofMillis(10), ofMillis(1000))\n<\/pre><\/div>\n\n\n<p>For our final example we make use of the<a href=\"https:\/\/github.com\/reactor\/reactor-addons\/#reactor-extra\"> reactor-extra<\/a> library. This library<br \/>consists additional operators. It also allows to create a Retry object which can be passed into the retryWhen() function. We use Retry.any() to retry all exception types. There are also functions to conditionally select when to retry (anyOf, allBut, onlyIf). The exponentialBackoff operator can be used to construct an exponential backoff strategy (to emulate a random backoff like the previous example use exponentialBackoffWithJitter):<br \/><\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: java; title: ; notranslate\" title=\"\">\ncallAPI().retryWhen(any().exponentialBackoff(ofMillis(10), ofMillis(1000)).retryMax(5)) \n<\/pre><\/div>\n\n\n<p>In case there are still errors after we are done retrying, a RetryExhaustedException is thrown. As an alternative to the exponentionalBackoff, reactor-extra also allows us to create a scheduled retry with <a href=\"https:\/\/projectreactor.io\/docs\/extra\/release\/api\/reactor\/retry\/Retry.html#withBackoffScheduler-reactor.core.scheduler.Scheduler-\">withBackoffScheduler<\/a>, a <a href=\"https:\/\/projectreactor.io\/docs\/extra\/release\/api\/reactor\/retry\/Retry.html#fixedBackoff-java.time.Duration-\">fixedBackoff<\/a> or a <a href=\"https:\/\/projectreactor.io\/docs\/extra\/release\/api\/reactor\/retry\/Retry.html#randomBackoff-java.time.Duration-java.time.Duration-\">randomBackoff<\/a>. <\/p>\n\n\n\n<h4 class=\"wp-block-heading\"> Conclusion <\/h4>\n\n\n\n<p>Project Reactor has several expressive solutions to construct retry logic. This allows us to have great control over how and when we retry certain pieces of logic without having to write much code. When you don\u2019t want to use an extra library, the core reactor library can be used to create a simpel retry statement. Additionaly the reactor-extra library gives us several different retry and backoff&nbsp; strategies.<\/p>\n\n\n\n<p><strong>More information from Trifork:<\/strong><\/p>\n\n\n\n<figure class=\"wp-block-image\"><a href=\"https:\/\/info.trifork.com\/cloud\" target=\"_blank\" rel=\"noopener\"><img loading=\"lazy\" decoding=\"async\" width=\"757\" height=\"528\" src=\"https:\/\/trifork.nl\/articles\/wp-content\/uploads\/sites\/3\/2019\/05\/image-3.png\" alt=\"\" class=\"wp-image-18801\" srcset=\"https:\/\/trifork.nl\/blog\/wp-content\/uploads\/sites\/3\/2019\/05\/image-3.png 757w, https:\/\/trifork.nl\/blog\/wp-content\/uploads\/sites\/3\/2019\/05\/image-3-300x209.png 300w\" sizes=\"auto, (max-width: 757px) 100vw, 757px\" \/><figcaption><a href=\"https:\/\/info.trifork.com\/cloud\"><\/a><\/a><\/figcaption><\/figure>\n\n\n\n<figure class=\"wp-block-image\"><a href=\"https:\/\/bit.ly\/3BAo305\" target=\"_blank\" rel=\"noreferrer noopener\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"256\" src=\"https:\/\/trifork.nl\/articles\/wp-content\/uploads\/sites\/3\/2022\/02\/Blog-Banner-1-1024x256.png\" alt=\"\" class=\"wp-image-20303\" srcset=\"https:\/\/trifork.nl\/blog\/wp-content\/uploads\/sites\/3\/2022\/02\/Blog-Banner-1-1024x256.png 1024w, https:\/\/trifork.nl\/blog\/wp-content\/uploads\/sites\/3\/2022\/02\/Blog-Banner-1-300x75.png 300w, https:\/\/trifork.nl\/blog\/wp-content\/uploads\/sites\/3\/2022\/02\/Blog-Banner-1-768x192.png 768w, https:\/\/trifork.nl\/blog\/wp-content\/uploads\/sites\/3\/2022\/02\/Blog-Banner-1-1536x384.png 1536w, https:\/\/trifork.nl\/blog\/wp-content\/uploads\/sites\/3\/2022\/02\/Blog-Banner-1-2048x512.png 2048w, https:\/\/trifork.nl\/blog\/wp-content\/uploads\/sites\/3\/2022\/02\/Blog-Banner-1-1920x480.png 1920w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/a><\/figure>\n","protected":false},"excerpt":{"rendered":"<p>Reactive programming can be used to solve a lot of different use cases. Forinstance, reactive programming can be really helpful for cases where timing is an issue. An example of such a case is retry logic with delay or backoff functionality. Let&#8217;s have a look at some different implementations using Project Reactor (https:\/\/projectreactor.io) for Java. [&hellip;]<\/p>\n","protected":false},"author":43,"featured_media":18346,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"content-type":"","footnotes":""},"categories":[31,10,94],"tags":[459],"class_list":["post-18324","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-java","category-development","category-spring","tag-reactive"],"acf":[],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v24.4 - https:\/\/yoast.com\/wordpress\/plugins\/seo\/ -->\n<title>Retry functionality in a reactive programming context - Trifork Blog<\/title>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/trifork.nl\/blog\/retry-functionality-in-a-reactive-programming-context\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Retry functionality in a reactive programming context - Trifork Blog\" \/>\n<meta property=\"og:description\" content=\"Reactive programming can be used to solve a lot of different use cases. Forinstance, reactive programming can be really helpful for cases where timing is an issue. An example of such a case is retry logic with delay or backoff functionality. Let&#8217;s have a look at some different implementations using Project Reactor (https:\/\/projectreactor.io) for Java. [&hellip;]\" \/>\n<meta property=\"og:url\" content=\"https:\/\/trifork.nl\/blog\/retry-functionality-in-a-reactive-programming-context\/\" \/>\n<meta property=\"og:site_name\" content=\"Trifork Blog\" \/>\n<meta property=\"article:published_time\" content=\"2019-03-13T08:12:34+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/trifork.nl\/blog\/wp-content\/uploads\/sites\/3\/2019\/03\/reactive-Retry-functionality-in-a-reactive-programming-context-featured.jpg\" \/>\n\t<meta property=\"og:image:width\" content=\"1735\" \/>\n\t<meta property=\"og:image:height\" content=\"585\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/jpeg\" \/>\n<meta name=\"author\" content=\"Erwin de Gier\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"Erwin de Gier\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"3 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"WebPage\",\"@id\":\"https:\/\/trifork.nl\/blog\/retry-functionality-in-a-reactive-programming-context\/\",\"url\":\"https:\/\/trifork.nl\/blog\/retry-functionality-in-a-reactive-programming-context\/\",\"name\":\"Retry functionality in a reactive programming context - Trifork Blog\",\"isPartOf\":{\"@id\":\"https:\/\/trifork.nl\/blog\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\/\/trifork.nl\/blog\/retry-functionality-in-a-reactive-programming-context\/#primaryimage\"},\"image\":{\"@id\":\"https:\/\/trifork.nl\/blog\/retry-functionality-in-a-reactive-programming-context\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/trifork.nl\/blog\/wp-content\/uploads\/sites\/3\/2019\/03\/reactive-Retry-functionality-in-a-reactive-programming-context-featured.jpg\",\"datePublished\":\"2019-03-13T08:12:34+00:00\",\"author\":{\"@id\":\"https:\/\/trifork.nl\/blog\/#\/schema\/person\/b1c52cf70084428baf0f1711c37b3fc2\"},\"breadcrumb\":{\"@id\":\"https:\/\/trifork.nl\/blog\/retry-functionality-in-a-reactive-programming-context\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/trifork.nl\/blog\/retry-functionality-in-a-reactive-programming-context\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/trifork.nl\/blog\/retry-functionality-in-a-reactive-programming-context\/#primaryimage\",\"url\":\"https:\/\/trifork.nl\/blog\/wp-content\/uploads\/sites\/3\/2019\/03\/reactive-Retry-functionality-in-a-reactive-programming-context-featured.jpg\",\"contentUrl\":\"https:\/\/trifork.nl\/blog\/wp-content\/uploads\/sites\/3\/2019\/03\/reactive-Retry-functionality-in-a-reactive-programming-context-featured.jpg\",\"width\":1735,\"height\":585},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/trifork.nl\/blog\/retry-functionality-in-a-reactive-programming-context\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/trifork.nl\/blog\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Retry functionality in a reactive programming context\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/trifork.nl\/blog\/#website\",\"url\":\"https:\/\/trifork.nl\/blog\/\",\"name\":\"Trifork Blog\",\"description\":\"Keep updated on the technical solutions Trifork is working on!\",\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/trifork.nl\/blog\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":\"Person\",\"@id\":\"https:\/\/trifork.nl\/blog\/#\/schema\/person\/b1c52cf70084428baf0f1711c37b3fc2\",\"name\":\"Erwin de Gier\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/trifork.nl\/blog\/#\/schema\/person\/image\/\",\"url\":\"https:\/\/secure.gravatar.com\/avatar\/7f8993fecf1561ed8bb0264270e42e73?s=96&d=mm&r=g\",\"contentUrl\":\"https:\/\/secure.gravatar.com\/avatar\/7f8993fecf1561ed8bb0264270e42e73?s=96&d=mm&r=g\",\"caption\":\"Erwin de Gier\"},\"url\":\"https:\/\/trifork.nl\/blog\/author\/erwindeg\/\"}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"Retry functionality in a reactive programming context - Trifork Blog","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/trifork.nl\/blog\/retry-functionality-in-a-reactive-programming-context\/","og_locale":"en_US","og_type":"article","og_title":"Retry functionality in a reactive programming context - Trifork Blog","og_description":"Reactive programming can be used to solve a lot of different use cases. Forinstance, reactive programming can be really helpful for cases where timing is an issue. An example of such a case is retry logic with delay or backoff functionality. Let&#8217;s have a look at some different implementations using Project Reactor (https:\/\/projectreactor.io) for Java. [&hellip;]","og_url":"https:\/\/trifork.nl\/blog\/retry-functionality-in-a-reactive-programming-context\/","og_site_name":"Trifork Blog","article_published_time":"2019-03-13T08:12:34+00:00","og_image":[{"width":1735,"height":585,"url":"https:\/\/trifork.nl\/blog\/wp-content\/uploads\/sites\/3\/2019\/03\/reactive-Retry-functionality-in-a-reactive-programming-context-featured.jpg","type":"image\/jpeg"}],"author":"Erwin de Gier","twitter_card":"summary_large_image","twitter_misc":{"Written by":"Erwin de Gier","Est. reading time":"3 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"WebPage","@id":"https:\/\/trifork.nl\/blog\/retry-functionality-in-a-reactive-programming-context\/","url":"https:\/\/trifork.nl\/blog\/retry-functionality-in-a-reactive-programming-context\/","name":"Retry functionality in a reactive programming context - Trifork Blog","isPartOf":{"@id":"https:\/\/trifork.nl\/blog\/#website"},"primaryImageOfPage":{"@id":"https:\/\/trifork.nl\/blog\/retry-functionality-in-a-reactive-programming-context\/#primaryimage"},"image":{"@id":"https:\/\/trifork.nl\/blog\/retry-functionality-in-a-reactive-programming-context\/#primaryimage"},"thumbnailUrl":"https:\/\/trifork.nl\/blog\/wp-content\/uploads\/sites\/3\/2019\/03\/reactive-Retry-functionality-in-a-reactive-programming-context-featured.jpg","datePublished":"2019-03-13T08:12:34+00:00","author":{"@id":"https:\/\/trifork.nl\/blog\/#\/schema\/person\/b1c52cf70084428baf0f1711c37b3fc2"},"breadcrumb":{"@id":"https:\/\/trifork.nl\/blog\/retry-functionality-in-a-reactive-programming-context\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/trifork.nl\/blog\/retry-functionality-in-a-reactive-programming-context\/"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/trifork.nl\/blog\/retry-functionality-in-a-reactive-programming-context\/#primaryimage","url":"https:\/\/trifork.nl\/blog\/wp-content\/uploads\/sites\/3\/2019\/03\/reactive-Retry-functionality-in-a-reactive-programming-context-featured.jpg","contentUrl":"https:\/\/trifork.nl\/blog\/wp-content\/uploads\/sites\/3\/2019\/03\/reactive-Retry-functionality-in-a-reactive-programming-context-featured.jpg","width":1735,"height":585},{"@type":"BreadcrumbList","@id":"https:\/\/trifork.nl\/blog\/retry-functionality-in-a-reactive-programming-context\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/trifork.nl\/blog\/"},{"@type":"ListItem","position":2,"name":"Retry functionality in a reactive programming context"}]},{"@type":"WebSite","@id":"https:\/\/trifork.nl\/blog\/#website","url":"https:\/\/trifork.nl\/blog\/","name":"Trifork Blog","description":"Keep updated on the technical solutions Trifork is working on!","potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/trifork.nl\/blog\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":"Person","@id":"https:\/\/trifork.nl\/blog\/#\/schema\/person\/b1c52cf70084428baf0f1711c37b3fc2","name":"Erwin de Gier","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/trifork.nl\/blog\/#\/schema\/person\/image\/","url":"https:\/\/secure.gravatar.com\/avatar\/7f8993fecf1561ed8bb0264270e42e73?s=96&d=mm&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/7f8993fecf1561ed8bb0264270e42e73?s=96&d=mm&r=g","caption":"Erwin de Gier"},"url":"https:\/\/trifork.nl\/blog\/author\/erwindeg\/"}]}},"_links":{"self":[{"href":"https:\/\/trifork.nl\/blog\/wp-json\/wp\/v2\/posts\/18324","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/trifork.nl\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/trifork.nl\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/trifork.nl\/blog\/wp-json\/wp\/v2\/users\/43"}],"replies":[{"embeddable":true,"href":"https:\/\/trifork.nl\/blog\/wp-json\/wp\/v2\/comments?post=18324"}],"version-history":[{"count":0,"href":"https:\/\/trifork.nl\/blog\/wp-json\/wp\/v2\/posts\/18324\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/trifork.nl\/blog\/wp-json\/wp\/v2\/media\/18346"}],"wp:attachment":[{"href":"https:\/\/trifork.nl\/blog\/wp-json\/wp\/v2\/media?parent=18324"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/trifork.nl\/blog\/wp-json\/wp\/v2\/categories?post=18324"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/trifork.nl\/blog\/wp-json\/wp\/v2\/tags?post=18324"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}