{"id":1243,"date":"2023-01-25T09:50:17","date_gmt":"2023-01-25T09:50:17","guid":{"rendered":"https:\/\/infobip.com\/developers\/?p=1243"},"modified":"2023-09-11T14:32:03","modified_gmt":"2023-09-11T14:32:03","slug":"pragmatic-use-of-java-records-and-optional","status":"publish","type":"post","link":"https:\/\/www.infobip.com\/developers\/blog\/pragmatic-use-of-java-records-and-optional","title":{"rendered":"Pragmatic use of Java Records and Optional"},"content":{"rendered":"\n<p>As Spring Boot 3 has raised the JDK requirement overnight to support at least Java 17, all code can now leverage newer language features.<\/p>\n\n\n\n<p>For bean classes, one newer language that comes into play in this context is Java Records.<\/p>\n\n\n\n<p>Some problems might appear when new mixes with the old, and one such example is mixing Records with good old (infamous) <code>java.util.Optional<\/code>.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"How-Optional-was-used-before-records\">How Optional was used before Records<\/h2>\n\n\n\n<p>Usually, Optional was used in code that also tries to be immutable:<\/p>\n\n\n\n<div class=\"hcb_wrap\"><pre class=\"prism line-numbers lang-java\" data-lang=\"Java\"><code>@Value\npublic class Request {\n    private final String optionalValue;\n \n    public Optional&lt;String&gt; getOptionalValue() {\n        return Optional.ofNullable(optionalValue);\n    }\n}<\/code><\/pre><\/div>\n\n\n\n<p>The reasoning is simple:<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li>In the wider community, using Optional as a field was considered a bad practice, but using it as a return type was considered valid use if used judiciously. There is, however, little consent on what counts as judicious, with some sources even claiming that it&#8217;s only allowed for JDK developers.<\/li>\n\n\n\n<li>It&#8217;s easier to integrate with various libraries, especially in the early days of Java 8 when many libraries didn&#8217;t have first-class support for Optional.<\/li>\n<\/ol>\n\n\n\n<p>One thing to note &#8211; even though Lombok&#8217;s&nbsp;<code>@Value<\/code>&nbsp; makes fields immutable, creates required properties constructor, getters, toString, and hashCode, in the instance above Lombok doesn&#8217;t generate a getter.<\/p>\n\n\n\n<p>A lengthy opinion piece has been published on whether Lombok should even have first-class support for Optional or not (<a href=\"https:\/\/github.com\/projectlombok\/lombok\/wiki\/Language-Design:-Null-vs.-Optional\">Language-Design:-Null-vs.-Optional<\/a>,&nbsp;<a href=\"https:\/\/github.com\/projectlombok\/lombok\/issues\/1957\">GitHub issue<\/a>).<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"On-the-Optional-controversy\">On the Optional controversy<\/h3>\n\n\n\n<p>Optional in Java 8 was quite controversial back in the day, with original creators going as far as saying this should only be used by JDK-provided APIs, meaning user code shouldn&#8217;t use Optional other than for local variables.<\/p>\n\n\n\n<p>Over time this opinion has softened quite a bit, and a few years ago, some Oracle employees actually clearly expressed that you should use Optional over null wherever possible.<\/p>\n\n\n\n<p>Still, all this controversy has warranted some against using it. And introduced some other,&nbsp;<a href=\"https:\/\/twitter.com\/gunnarmorling\/status\/1370313303214456833\">uninvited<\/a>, to the discussion.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"Records\">Java Records<\/h2>\n\n\n\n<p>Now, back to the people in the real world, trying to solve real problems&#8230;<\/p>\n\n\n\n<p>If one tried to apply&nbsp;<code>@Value<\/code>&nbsp;pattern of only using Optional on a getter, the end result, when combined with Records, would probably look something like this:<\/p>\n\n\n\n<div class=\"hcb_wrap\"><pre class=\"prism line-numbers lang-java\" data-lang=\"Java\"><code>public record Request(String optionalValue) {\n \n    public Optional&lt;String&gt; optionalValue() {\n        return Optional.ofNullable(optionalValue);\n    }\n}<\/code><\/pre><\/div>\n\n\n\n<p>and they would probably have gotten away with it if it hadn&#8217;t been for that meddling compiler:<\/p>\n\n\n\n<div class=\"hcb_wrap\"><pre class=\"prism line-numbers lang-java\" data-lang=\"Java\"><code>java: invalid accessor method in record com.infobip.message.viber.Request\n \n  (return type of accessor method optionalValue() must match the type of record component optionalValue)<\/code><\/pre><\/div>\n\n\n\n<p>In plain English &#8211; the input type for optionalValue must be the same for a constructor and accessor (`optionalValue()`).<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"Show-me-the-code\">Show me the code<\/h2>\n\n\n\n<p>So the only way to use Optional with Records is to declare the property to be of type Optional:<\/p>\n\n\n\n<div class=\"hcb_wrap\"><pre class=\"prism line-numbers lang-java\" data-lang=\"Java\"><code>public record Request(Optional&lt;String&gt; optionalValue) {\n}<\/code><\/pre><\/div>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"Optional-as-an-argument\">But I don&#8217;t want to pass Optional as an argument<\/h3>\n\n\n\n<p>There&#8217;s little discussion about this issue &#8211; a constructor must accept the same type as accessor returns.<\/p>\n\n\n\n<p>Now before you start typing custom constructors or static factory methods to hide the fact, there is a framework that alleviates the issue somewhat. Still, I&#8217;d advise only using it when necessary (meaning many optional parameters):<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"Record-builder\">Record builder<\/h3>\n\n\n\n<p><a href=\"https:\/\/github.com\/Randgalt\/record-builder\">Record builder<\/a>&nbsp;is yet another annotation processor that automates the creation of Record builder with some additional bells and whistles.<\/p>\n\n\n\n<p>Usually, I use it like this:<\/p>\n\n\n\n<div class=\"hcb_wrap\"><pre class=\"prism line-numbers lang-java\" data-lang=\"Java\"><code>@RecordBuilder.Template(options = @RecordBuilder.Options(\n    addConcreteSettersForOptional = true\n))\n@Retention(RetentionPolicy.SOURCE)\n@Target(ElementType.TYPE)\n@Inherited\npublic @interface CommonRecordBuilder {\n \n}<\/code><\/pre><\/div>\n\n\n\n<p>Now Request call site can use code like this:<\/p>\n\n\n\n<div class=\"hcb_wrap\"><pre class=\"prism line-numbers lang-java\" data-lang=\"Java\"><code>\/\/ given\nString value = null;\nvar builder = RequestBuilder.builder();\n \n\/\/ when\nbuilder.optionalValue(value);\n \n\/\/ then\nthen(builder.build()).isEqualTo(new Request(Optional.empty()));<\/code><\/pre><\/div>\n\n\n\n<p>and not deal with <code>Optional<\/code> parameters.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"Jackson-support\">Jackson support<\/h3>\n\n\n\n<p>Jackson has supported Java Records for over&nbsp;<a href=\"https:\/\/github.com\/FasterXML\/jackson-databind\/issues\/2709\">two years now<\/a>, since version 2.12.0. Needless to say, if you&#8217;re on Spring Boot 3, you&#8217;re probably on a newer version of Jackson.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"Bean-Validation\">Bean Validation<\/h3>\n\n\n\n<p>Bean validation is usually used by annotating fields.&nbsp;<a href=\"https:\/\/docs.jboss.org\/hibernate\/stable\/validator\/reference\/en-US\/html_single\/#_with_java_util_optional\">Since Bean Validation doesn&#8217;t work the same with Optional&lt;T&gt; as with just T<\/a>,&nbsp;there are certain changes that need to be done:<\/p>\n\n\n\n<div class=\"hcb_wrap\"><pre class=\"prism line-numbers lang-java\" data-lang=\"Java\"><code>@Value\npublic class Request {\n    @Size(max = 100)\n    private final String optionalValue;\n \n    public Optional&lt;String&gt; getOptionalValue() {\n        return Optional.ofNullable(optionalValue);\n    }\n}<\/code><\/pre><\/div>\n\n\n\n<div class=\"hcb_wrap\"><pre class=\"prism line-numbers lang-java\" data-lang=\"Java\"><code>public record Request(Optional&lt;@Size(max = 100) String&gt; optionalValue) {\n \n}<\/code><\/pre><\/div>\n\n\n\n<p>An important bit is that&nbsp;<code>@Size<\/code>&nbsp;is inlined into the generic type signature.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"OpenAPI\">OpenAPI<\/h3>\n\n\n\n<p>OpenAPI, Bean Validation, Records, and Optional currently don&#8217;t play well together. Or, to be more correct,&nbsp;<a href=\"https:\/\/github.com\/springdoc\/springdoc-openapi\/issues\/2042\">Springdoc doesn&#8217;t<\/a>.<\/p>\n\n\n\n<p>In short, Springdoc doesn&#8217;t correctly recognize Optional as a wrapper type. I expect Springdoc maintainers will fix the issue.<\/p>\n\n\n\n<h1 class=\"wp-block-heading\" id=\"A-way-forward\">A way forward<\/h1>\n\n\n\n<p>Based on the&nbsp;current Java roadmap,&nbsp;it&#8217;s clear that the future is going more in a direction toward the Records, with value (or inline) types being one of the goals down that road.<\/p>\n\n\n\n<p>The old way of defining classes with Lombok&#8217;s&nbsp;<code>@Value<\/code>&nbsp; is quickly becoming obsolete and should be avoided wherever possible.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>When new mixes with the old, some problems make appear, and one such example is mixing records with good old (infamous) java.util.Optional.<\/p>\n","protected":false},"author":17,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"_import_markdown_pro_load_document_selector":0,"_import_markdown_pro_submit_text_textarea":"","footnotes":""},"categories":[28,254,252],"tags":[144,256],"coauthors":[162],"class_list":["post-1243","post","type-post","status-publish","format-standard","hentry","category-blog-post","category-engineering-practices","category-tools","tag-developer-ecosystem","tag-programming"],"acf":[],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v25.6 - https:\/\/yoast.com\/wordpress\/plugins\/seo\/ -->\n<title>Java Records and Optional: A Pragmatic use<\/title>\n<meta name=\"description\" content=\"When new mixes with the old, some problems make appear, and one such example is mixing Java records with good old java.util.Optional.\" \/>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/www.infobip.com\/developers\/blog\/pragmatic-use-of-java-records-and-optional\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Infobip Developers Hub\" \/>\n<meta property=\"og:description\" content=\"When new mixes with the old, some problems make appear, and one such example is mixing records with good old (infamous) java.util.Optional.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/www.infobip.com\/developers\/blog\/pragmatic-use-of-java-records-and-optional\" \/>\n<meta property=\"og:site_name\" content=\"Infobip Developers Hub\" \/>\n<meta property=\"article:publisher\" content=\"https:\/\/www.facebook.com\/infobip\/\" \/>\n<meta property=\"article:published_time\" content=\"2023-01-25T09:50:17+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2023-09-11T14:32:03+00:00\" \/>\n<meta name=\"author\" content=\"Lovro Pandzic\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:title\" content=\"Pragmatic use of Java Records and Optional\" \/>\n<meta name=\"twitter:description\" content=\"When new mixes with the old, some problems make appear, and one such example is mixing records with good old (infamous) java.util.Optional.\" \/>\n<meta name=\"twitter:creator\" content=\"@InfobipDev\" \/>\n<meta name=\"twitter:site\" content=\"@InfobipDev\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"Lovro Pandzic\" \/>\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\":\"Article\",\"@id\":\"https:\/\/www.infobip.com\/developers\/blog\/pragmatic-use-of-java-records-and-optional#article\",\"isPartOf\":{\"@id\":\"https:\/\/www.infobip.com\/developers\/blog\/pragmatic-use-of-java-records-and-optional\"},\"author\":{\"name\":\"Lovro Pandzic\",\"@id\":\"https:\/\/www.infobip.com\/developers\/#\/schema\/person\/5dda6c6a6a89222bbe7053ba3a78391f\"},\"headline\":\"Pragmatic use of Java Records and Optional\",\"datePublished\":\"2023-01-25T09:50:17+00:00\",\"dateModified\":\"2023-09-11T14:32:03+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\/\/www.infobip.com\/developers\/blog\/pragmatic-use-of-java-records-and-optional\"},\"wordCount\":693,\"publisher\":{\"@id\":\"https:\/\/www.infobip.com\/developers\/#organization\"},\"keywords\":[\"developer ecosystem\",\"programming\"],\"articleSection\":[\"Blog Post\",\"Engineering Practices\",\"Tools\"],\"inLanguage\":\"en-US\"},{\"@type\":\"WebPage\",\"@id\":\"https:\/\/www.infobip.com\/developers\/blog\/pragmatic-use-of-java-records-and-optional\",\"url\":\"https:\/\/www.infobip.com\/developers\/blog\/pragmatic-use-of-java-records-and-optional\",\"name\":\"Java Records and Optional: A Pragmatic use\",\"isPartOf\":{\"@id\":\"https:\/\/www.infobip.com\/developers\/#website\"},\"datePublished\":\"2023-01-25T09:50:17+00:00\",\"dateModified\":\"2023-09-11T14:32:03+00:00\",\"description\":\"When new mixes with the old, some problems make appear, and one such example is mixing Java records with good old java.util.Optional.\",\"breadcrumb\":{\"@id\":\"https:\/\/www.infobip.com\/developers\/blog\/pragmatic-use-of-java-records-and-optional#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/www.infobip.com\/developers\/blog\/pragmatic-use-of-java-records-and-optional\"]}]},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/www.infobip.com\/developers\/blog\/pragmatic-use-of-java-records-and-optional#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/www.infobip.com\/developers\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Pragmatic use of Java Records and Optional\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/www.infobip.com\/developers\/#website\",\"url\":\"https:\/\/www.infobip.com\/developers\/\",\"name\":\"Infobip Developers Hub\",\"description\":\"Build meaningful customer relationships across any channel\",\"publisher\":{\"@id\":\"https:\/\/www.infobip.com\/developers\/#organization\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/www.infobip.com\/developers\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":\"Organization\",\"@id\":\"https:\/\/www.infobip.com\/developers\/#organization\",\"name\":\"Infobip Developers Hub\",\"url\":\"https:\/\/www.infobip.com\/developers\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/www.infobip.com\/developers\/#\/schema\/logo\/image\/\",\"url\":\"https:\/\/www.infobip.com\/developers\/wp-content\/uploads\/2023\/03\/Infobip_logo_favicon.png\",\"contentUrl\":\"https:\/\/www.infobip.com\/developers\/wp-content\/uploads\/2023\/03\/Infobip_logo_favicon.png\",\"width\":696,\"height\":696,\"caption\":\"Infobip Developers Hub\"},\"image\":{\"@id\":\"https:\/\/www.infobip.com\/developers\/#\/schema\/logo\/image\/\"},\"sameAs\":[\"https:\/\/www.facebook.com\/infobip\/\",\"https:\/\/x.com\/InfobipDev\",\"https:\/\/www.youtube.com\/channel\/UCUPSTy53VecI5GIir3J3ZbQ\",\"https:\/\/github.com\/infobip-community\"]},{\"@type\":\"Person\",\"@id\":\"https:\/\/www.infobip.com\/developers\/#\/schema\/person\/5dda6c6a6a89222bbe7053ba3a78391f\",\"name\":\"Lovro Pandzic\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/www.infobip.com\/developers\/#\/schema\/person\/image\/7da0171aecfe507fb08f9614fa328272\",\"url\":\"https:\/\/secure.gravatar.com\/avatar\/49704dcf94a32521b6c2025add6620c0a4f81f2e89d01c80f920130f17dd0852?s=96&d=mm&r=g\",\"contentUrl\":\"https:\/\/secure.gravatar.com\/avatar\/49704dcf94a32521b6c2025add6620c0a4f81f2e89d01c80f920130f17dd0852?s=96&d=mm&r=g\",\"caption\":\"Lovro Pandzic\"},\"description\":\"Lovro is a principal Engineer at Infobip, working in the SaaS BA team.\",\"sameAs\":[\"https:\/\/hr.linkedin.com\/in\/lovropandzic\"],\"url\":\"https:\/\/www.infobip.com\/developers\/blog\/author\/lovro-pandzic\"}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"Java Records and Optional: A Pragmatic use","description":"When new mixes with the old, some problems make appear, and one such example is mixing Java records with good old java.util.Optional.","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:\/\/www.infobip.com\/developers\/blog\/pragmatic-use-of-java-records-and-optional","og_locale":"en_US","og_type":"article","og_title":"Infobip Developers Hub","og_description":"When new mixes with the old, some problems make appear, and one such example is mixing records with good old (infamous) java.util.Optional.","og_url":"https:\/\/www.infobip.com\/developers\/blog\/pragmatic-use-of-java-records-and-optional","og_site_name":"Infobip Developers Hub","article_publisher":"https:\/\/www.facebook.com\/infobip\/","article_published_time":"2023-01-25T09:50:17+00:00","article_modified_time":"2023-09-11T14:32:03+00:00","author":"Lovro Pandzic","twitter_card":"summary_large_image","twitter_title":"Pragmatic use of Java Records and Optional","twitter_description":"When new mixes with the old, some problems make appear, and one such example is mixing records with good old (infamous) java.util.Optional.","twitter_creator":"@InfobipDev","twitter_site":"@InfobipDev","twitter_misc":{"Written by":"Lovro Pandzic","Est. reading time":"3 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/www.infobip.com\/developers\/blog\/pragmatic-use-of-java-records-and-optional#article","isPartOf":{"@id":"https:\/\/www.infobip.com\/developers\/blog\/pragmatic-use-of-java-records-and-optional"},"author":{"name":"Lovro Pandzic","@id":"https:\/\/www.infobip.com\/developers\/#\/schema\/person\/5dda6c6a6a89222bbe7053ba3a78391f"},"headline":"Pragmatic use of Java Records and Optional","datePublished":"2023-01-25T09:50:17+00:00","dateModified":"2023-09-11T14:32:03+00:00","mainEntityOfPage":{"@id":"https:\/\/www.infobip.com\/developers\/blog\/pragmatic-use-of-java-records-and-optional"},"wordCount":693,"publisher":{"@id":"https:\/\/www.infobip.com\/developers\/#organization"},"keywords":["developer ecosystem","programming"],"articleSection":["Blog Post","Engineering Practices","Tools"],"inLanguage":"en-US"},{"@type":"WebPage","@id":"https:\/\/www.infobip.com\/developers\/blog\/pragmatic-use-of-java-records-and-optional","url":"https:\/\/www.infobip.com\/developers\/blog\/pragmatic-use-of-java-records-and-optional","name":"Java Records and Optional: A Pragmatic use","isPartOf":{"@id":"https:\/\/www.infobip.com\/developers\/#website"},"datePublished":"2023-01-25T09:50:17+00:00","dateModified":"2023-09-11T14:32:03+00:00","description":"When new mixes with the old, some problems make appear, and one such example is mixing Java records with good old java.util.Optional.","breadcrumb":{"@id":"https:\/\/www.infobip.com\/developers\/blog\/pragmatic-use-of-java-records-and-optional#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/www.infobip.com\/developers\/blog\/pragmatic-use-of-java-records-and-optional"]}]},{"@type":"BreadcrumbList","@id":"https:\/\/www.infobip.com\/developers\/blog\/pragmatic-use-of-java-records-and-optional#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/www.infobip.com\/developers\/"},{"@type":"ListItem","position":2,"name":"Pragmatic use of Java Records and Optional"}]},{"@type":"WebSite","@id":"https:\/\/www.infobip.com\/developers\/#website","url":"https:\/\/www.infobip.com\/developers\/","name":"Infobip Developers Hub","description":"Build meaningful customer relationships across any channel","publisher":{"@id":"https:\/\/www.infobip.com\/developers\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/www.infobip.com\/developers\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":"Organization","@id":"https:\/\/www.infobip.com\/developers\/#organization","name":"Infobip Developers Hub","url":"https:\/\/www.infobip.com\/developers\/","logo":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.infobip.com\/developers\/#\/schema\/logo\/image\/","url":"https:\/\/www.infobip.com\/developers\/wp-content\/uploads\/2023\/03\/Infobip_logo_favicon.png","contentUrl":"https:\/\/www.infobip.com\/developers\/wp-content\/uploads\/2023\/03\/Infobip_logo_favicon.png","width":696,"height":696,"caption":"Infobip Developers Hub"},"image":{"@id":"https:\/\/www.infobip.com\/developers\/#\/schema\/logo\/image\/"},"sameAs":["https:\/\/www.facebook.com\/infobip\/","https:\/\/x.com\/InfobipDev","https:\/\/www.youtube.com\/channel\/UCUPSTy53VecI5GIir3J3ZbQ","https:\/\/github.com\/infobip-community"]},{"@type":"Person","@id":"https:\/\/www.infobip.com\/developers\/#\/schema\/person\/5dda6c6a6a89222bbe7053ba3a78391f","name":"Lovro Pandzic","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.infobip.com\/developers\/#\/schema\/person\/image\/7da0171aecfe507fb08f9614fa328272","url":"https:\/\/secure.gravatar.com\/avatar\/49704dcf94a32521b6c2025add6620c0a4f81f2e89d01c80f920130f17dd0852?s=96&d=mm&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/49704dcf94a32521b6c2025add6620c0a4f81f2e89d01c80f920130f17dd0852?s=96&d=mm&r=g","caption":"Lovro Pandzic"},"description":"Lovro is a principal Engineer at Infobip, working in the SaaS BA team.","sameAs":["https:\/\/hr.linkedin.com\/in\/lovropandzic"],"url":"https:\/\/www.infobip.com\/developers\/blog\/author\/lovro-pandzic"}]}},"_links":{"self":[{"href":"https:\/\/www.infobip.com\/developers\/wp-json\/wp\/v2\/posts\/1243","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.infobip.com\/developers\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.infobip.com\/developers\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.infobip.com\/developers\/wp-json\/wp\/v2\/users\/17"}],"replies":[{"embeddable":true,"href":"https:\/\/www.infobip.com\/developers\/wp-json\/wp\/v2\/comments?post=1243"}],"version-history":[{"count":30,"href":"https:\/\/www.infobip.com\/developers\/wp-json\/wp\/v2\/posts\/1243\/revisions"}],"predecessor-version":[{"id":2125,"href":"https:\/\/www.infobip.com\/developers\/wp-json\/wp\/v2\/posts\/1243\/revisions\/2125"}],"wp:attachment":[{"href":"https:\/\/www.infobip.com\/developers\/wp-json\/wp\/v2\/media?parent=1243"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.infobip.com\/developers\/wp-json\/wp\/v2\/categories?post=1243"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.infobip.com\/developers\/wp-json\/wp\/v2\/tags?post=1243"},{"taxonomy":"author","embeddable":true,"href":"https:\/\/www.infobip.com\/developers\/wp-json\/wp\/v2\/coauthors?post=1243"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}