{"id":382514,"date":"2024-03-07T10:25:27","date_gmt":"2024-03-07T16:25:27","guid":{"rendered":"https:\/\/wordpress.appsilon.com\/?p=23597"},"modified":"2024-03-07T10:25:27","modified_gmt":"2024-03-07T16:25:27","slug":"r-plumber-how-to-craft-error-responses-that-speak-fluent-http","status":"publish","type":"post","link":"https:\/\/www.r-bloggers.com\/2024\/03\/r-plumber-how-to-craft-error-responses-that-speak-fluent-http\/","title":{"rendered":"R Plumber: How to Craft Error Responses that Speak Fluent HTTP"},"content":{"rendered":"<!-- \r\n<div style=\"min-height: 30px;\">\r\n[social4i size=\"small\" align=\"align-left\"]\r\n<\/div>\r\n-->\r\n\r\n<div style=\"border: 1px solid; background: none repeat scroll 0 0 #EDEDED; margin: 1px; font-size: 12px;\">\r\n[This article was first published on  <strong><a href=\"https:\/\/appsilon.com\/api-oopsies-101\/\"> Tag: r - Appsilon | Enterprise R Shiny Dashboards<\/a><\/strong>, and kindly contributed to <a href=\"https:\/\/www.r-bloggers.com\/\" rel=\"nofollow\">R-bloggers<\/a>].  (You can report issue about the content on this page <a href=\"https:\/\/www.r-bloggers.com\/contact-us\/\">here<\/a>)\r\n<hr>Want to share your content on R-bloggers?<a href=\"https:\/\/www.r-bloggers.com\/add-your-blog\/\" rel=\"nofollow\"> click here<\/a> if you have a blog, or <a href=\"http:\/\/r-posts.com\/\" rel=\"nofollow\"> here<\/a> if you don't.\r\n<\/div>\n<div><img width=\"450\" src=\"https:\/\/wordpress.appsilon.com\/wp-content\/uploads\/2024\/03\/R-Plumber-How-to-Craft-Error-Responses-that-Speak-Fluent-HTTP.webp\" class=\"attachment-medium size-medium wp-post-image\" alt=\"\" decoding=\"async\" style=\"margin-bottom: 15px;\" loading=\"lazy\" \/><\/div><p><span style=\"font-weight: 400;\">APIs (Application Programming Interfaces) are the backbone of modern software development, enabling seamless communication between different applications or components. <\/span><\/p>\n<p><span style=\"font-weight: 400;\">However, like any technology, APIs are <strong>not immune to errors. <\/strong><\/span><span style=\"font-weight: 400;\">In this brief guide, we\u2019ll explore some common HTTP status codes and shed light on this crucial aspect of web API development with R Plumber. <\/span><\/p>\n<p><span style=\"font-weight: 400;\">Following the R way of handling errors, we will dive into the internals of error signaling, conditions, and handlers. Using this knowledge we will build on {<\/span><a href=\"https:\/\/appsilon.com\/r-rest-api\/\" rel=\"nofollow\" target=\"_blank\"><span style=\"font-weight: 400;\">plumber<\/span><\/a><span style=\"font-weight: 400;\">}\u2019s error handling utilities to signal errors to the client in a tidy and flexible way from any point in the lifecycle of the request processing.<\/span><\/p>\n<p>But first, let\u2019s go over the most common HTTP status codes and explain what they mean.<\/p>\n<blockquote><p>Entirely new to Plumber?\u00a0<a href=\"https:\/\/appsilon.com\/r-rest-api\/\" rel=\"nofollow\" target=\"_blank\">Check out our beginner-friendly guide to making REST APIs in R.<\/a><\/p><\/blockquote>\n<h3>Table of Contents<\/h3>\n<ul>\n<li><strong><a href=\"https:\/\/appsilon.com\/api-oopsies-101\/#common-http-status-codes\" rel=\"nofollow\" target=\"_blank\">Common HTTP Status Codes<\/a><\/strong><\/li>\n<li><strong><a href=\"https:\/\/appsilon.com\/api-oopsies-101\/#the-r-way-best-practices-for-error-handling\" rel=\"nofollow\" target=\"_blank\">The R Way: Best Practices for Error Handling<\/a><\/strong><\/li>\n<li><strong><a href=\"https:\/\/appsilon.com\/api-oopsies-101\/#error-handlers-in-plumber\" rel=\"nofollow\" target=\"_blank\">Error Handlers in {plumber}<\/a><\/strong><\/li>\n<li><strong><a href=\"https:\/\/appsilon.com\/api-oopsies-101\/#summarizing-api-oopsies-101\" rel=\"nofollow\" target=\"_blank\">Summing up R Plumber Custom Error Messages<\/a><\/strong><\/li>\n<\/ul>\n<hr \/>\n<h2 id=\"common-http-status-codes\">Common HTTP Status Codes<\/h2>\n<p>Common HTTP status codes encompass a range of issues spanning from request errors, such as <em>Bad Request<\/em> or <em>Forbidden<\/em>, to transient errors, like <em>Service Unavailable<\/em>, providing insight into the health and functionality of the system.<\/p>\n<ol>\n<li><strong>Error 400 \u2013 Bad Request:<\/strong> This status code indicates that the server cannot process the request due to a client error. It might be caused by malformed requests, missing parameters, or invalid input.<\/li>\n<li><strong>Error 401 \u2013 Unauthorized:<\/strong> The server understands the request, but the client must authenticate itself to get the requested response. In simpler terms, the user lacks the necessary credentials to access the resource.<\/li>\n<li><strong>Error 403 \u2013 Forbidden:<\/strong> Similar to 401, but in this case, even with authentication, the client doesn\u2019t have permission to access the requested resource. It\u2019s a security measure to prevent unauthorized access.<\/li>\n<li><strong>Error 404 \u2013 Not Found:<\/strong> One of the most recognizable errors, 404 indicates that the server can\u2019t find the requested resource. This could be due to a mistyped URL or the resource being temporarily or permanently removed.<\/li>\n<li><strong>Error 500 \u2013 Internal Server Error:<\/strong> A generic error message indicating that the server encountered an unexpected condition preventing it from fulfilling the request. This could be due to bugs in the server-side code or issues with the server\u2019s configuration.<\/li>\n<\/ol>\n<p>And now with this knowledge out of the way, let\u2019s briefly discuss some other types of errors you\u2019re likely to run into when building APIs with R Plumber.<\/p>\n<h3>Rate Limits and Transient Errors<\/h3>\n<p>API providers often impose rate limits to <strong>control the number of requests<\/strong> a client can make within a specific time frame. Transient errors are temporary glitches in the API\u2019s functionality. They might occur due to network issues, server overloads, or momentary service disruptions.<\/p>\n<p>The API should provide documentation for developers so that they can implement appropriate strategies, such as exponential backoff, to handle rate-limited scenarios gracefully.<\/p>\n<p>Some common status code examples are:<\/p>\n<ol>\n<li><strong>Error 503 \u2013 Service Unavailable:<\/strong> The server is temporarily unable to handle the request, usually due to maintenance or overloading. This status code suggests that the service may become available again, and developers should implement retry mechanisms with appropriate back-off strategies to manage these temporary unavailability periods.<\/li>\n<li><strong>Error 429 \u2013 Too Many Requests:<\/strong> This status code signals that the client has sent too many requests within a given time frame, exceeding the rate limit set by the API provider. Developers should implement strategies such as exponential backoff to handle rate-limited scenarios.<\/li>\n<\/ol>\n<p>You now know what these errors mean, but how can you handle them in R and R Plumber? That\u2019s what we\u2019ll discuss next.<\/p>\n<blockquote><p>You can run your R scripts or even R Plumber APIs in Docker \u2013 <a href=\"https:\/\/appsilon.com\/r-docker-getting-started\/\" rel=\"nofollow\" target=\"_blank\">Follow our new guide to get started.<\/a><\/p><\/blockquote>\n<h2 id=\"the-r-way-best-practices-for-error-handling\">The R Way: Best Practices for Error Handling in R Plumber<\/h2>\n<p>In R, unusual events are signaled and handled using <a href=\"https:\/\/adv-r.hadley.nz\/conditions.html\" rel=\"nofollow\" target=\"_blank\">conditions<\/a>. These form a hierarchy with three main categories: errors, warnings, and messages. Errors indicate critical issues that typically halt the execution of code, warnings signify potential problems that don\u2019t necessarily halt execution but should be addressed, and messages provide informative feedback without affecting code execution.<\/p>\n<p>To understand the basics of error signaling in R, let\u2019s focus on two base functions: <code>simpleError()<\/code> and <code>stop()<\/code>. These functions are used in the context of handling critical failures in the execution flow of a program, but they serve slightly different purposes:<\/p>\n<ul>\n<li><strong>simpleError()<\/strong> is a function that creates a condition object. This object represents an error state and includes information about the error, such as an error message and a class. However, <code>simpleError()<\/code> by itself does not halt execution or signal the error condition to the R interpreter. It\u2019s essentially a way to create a standardized error object that can then be used with stop() or other condition-handling mechanisms.<\/li>\n<li><strong>stop()<\/strong> function both creates an error condition and immediately signals it, which interrupts the normal flow of execution and exits from the current function or expression with an error. The <code>stop()<\/code> function can take an error message directly, or it can take an error object created by <code>simpleError()<\/code>. When <code>stop()<\/code> is called, it halts the execution of the program and, unless caught by a condition handling construct like <code>tryCatch()<\/code>, will terminate the execution of the current expression and propagate the error up the call stack.<\/li>\n<\/ul>\n<p>In the <a href=\"https:\/\/www.rplumber.io\/\" rel=\"nofollow\" target=\"_blank\">R Plumber<\/a>\u00a0framework, all errors originating from endpoints or filters are captured and passed to an error handler for processing as condition objects. This default handler responds with a status code of 500 (Internal Server Error).<\/p>\n<p>However, we have the flexibility to implement our own error-handling function. This custom function allows us to examine the <strong>error condition metadata<\/strong> and decide on the most suitable response code to return to the client. Our goal is to manage errors gracefully and to deliver a clear and helpful error response to the client.<\/p>\n<p><em>But how can we equip these error conditions with metadata?<\/em><\/p>\n<p>In the R programming language, conditions can be equipped with metadata, providing additional context and information about the event. This metadata is particularly useful in the context of classed conditions, a concept rooted in R\u2019s S3 system.<\/p>\n<blockquote><p>Eager to grasp the essence of S3 OOP in R? <a href=\"https:\/\/appsilon.com\/object-oriented-programming-in-r-part-2\/\" rel=\"nofollow\" target=\"_blank\">Don\u2019t miss out on Part 2 of our series for simplified insights.<\/a><\/p><\/blockquote>\n<p>Classed conditions allow for the creation of custom condition classes, each tailored to represent a specific type of error or exceptional situation.<\/p>\n<p><strong>By assigning a class to a condition, developers can impart semantic meaning to the error, facilitating more targeted handling and interpretation.<\/strong> Leveraging this approach, we will standardize how we handle API error responses, ensuring uniformity and robustness in handling different categories of errors (494, 401, 503, etc.).<\/p>\n<h3>Classes Error Example<\/h3>\n<pre>\r\nerror_bad_argument &lt;- errorCondition(&quot;Non-numeric input&quot;, class = &quot;bad_argument&quot;)\r\n\r\nmy_log &lt;- function(x) {\r\n  if(!is.numeric(x)) stop(error_bad_argument)\r\n  log(x)\r\n}\r\n\r\n# Handle the \u2018bad_argument\u2019 classed error\r\ntryCatch(\r\n  my_log(&quot;a&quot;),\r\n  bad_argument = function(cnd) &quot;handled bad_argument&quot;,\r\n  error = function(cnd) &quot;other error&quot;\r\n)\r\n<\/pre>\n<p>Or using {rlang} equivalently:<\/p>\n<pre>\r\nlibrary(rlang)\r\n\r\nmy_log &lt;- function(x) {\r\n  if(!is.numeric(x)) abort(&quot;Non-numeric input&quot;, class = &quot;bad_argument&quot;)\r\n  log(x)\r\n}\r\n\r\n# Handle the \u2018bad_argument\u2019 classed error:\r\ntry_fetch(\r\n  my_log(&quot;a&quot;),\r\n  bad_argument = function(cnd) &quot;handled bad_argument&quot;,\r\n  error = function(cnd) &quot;other error&quot;\r\n)\r\n<\/pre>\n<p>By executing the above code the classed error returned by <code>my_log()<\/code> is handled by <code>bad_argument<\/code> handler in <code>try_fetch()<\/code>, returning:<\/p>\n<p><div id=\"attachment_23602\" style=\"width: 561px\" class=\"wp-caption aligncenter\"><img loading=\"lazy\" aria-describedby=\"caption-attachment-23602\" decoding=\"async\" class=\" wp-image-23602\" src=\"https:\/\/wordpress.appsilon.com\/wp-content\/uploads\/2024\/03\/img4.webp\" alt=\"A snippet of code output in a console displaying the text '[1] \"handled bad_argument\"'.\" width=\"450\" \/><p id=\"caption-attachment-23602\" class=\"wp-caption-text\">Console Output Indicating Handled Exception<\/p><\/div>To delve deeper into the realm of conditions and to learn more about adding contextual information to errors you can find excellent examples in <a href=\"https:\/\/rdrr.io\/r\/base\/conditions.html\" rel=\"nofollow\" target=\"_blank\">base R documentation<\/a> and in {<a href=\"https:\/\/rlang.r-lib.org\/reference\/topic-error-chaining.html\" rel=\"nofollow\" target=\"_blank\">rlang<\/a>}.<\/p>\n<h2 id=\"error-handlers-in-plumber\">Error Handlers in R Plumber<\/h2>\n<h3>Custom 404 Handler in {plumber}<\/h3>\n<p>In the provided code snippet below, we demonstrate the creation of a custom handler for <em>404 Not Found errors<\/em>. {plumber} treats this type of error separately using the dedicated function <code>pr_set_404()<\/code>:<\/p>\n<pre>\r\nlibrary(plumber)\r\n\r\nhandler_404 &lt;- function(req, res) {\r\n  res$status &lt;- 404\r\n  res$body &lt;- &quot;Can't find the requested resource&quot; \r\n} \r\n\r\n# Create Plumber router \r\npr() |&gt;\r\n  pr_get(&quot;\/hi&quot;, function() &quot;Hello&quot;) |&gt;\r\n  pr_set_404(handler_404) |&gt;\r\n  pr_run()\r\n<\/pre>\n<p>Now, if you visit the server URL of {plumber} requesting a resource that doesn\u2019t exist, {plumber} will return a status code 404 with our custom message.<\/p>\n<div id=\"attachment_23606\" style=\"width: 1019px\" class=\"wp-caption aligncenter\"><img loading=\"lazy\" aria-describedby=\"caption-attachment-23606\" decoding=\"async\" class=\"size-full wp-image-23606\" src=\"https:\/\/i2.wp.com\/wordpress.appsilon.com\/wp-content\/uploads\/2024\/03\/img1.png?w=450&#038;ssl=1\" alt=\"Error message in a web browser indicating a 404 Not Found error for a resource on a local server with a network console open.\" data-recalc-dims=\"1\" \/><p id=\"caption-attachment-23606\" class=\"wp-caption-text\">404 Not Found Error in Browser<\/p><\/div>\n<h3>Custom Error Handling with <code>pr_set_error()<\/code><\/h3>\n<p>The <code>pr_set_error()<\/code> in {plumber} enables streamlined API error handling. By defining a custom error handler, such as <code>handler_error()<\/code> in the snippet below, developers can effectively manage errors that arise during client requests. This handler specifies the HTTP status code and provides a concise error message, ensuring consistent and informative responses to clients.<\/p>\n<pre>\r\nlibrary(plumber)\r\n\r\nhandler_error &lt;- function(req, res, err) {\r\n  res$status &lt;- 500 \r\n  list(error = &quot;Custom Error Message&quot;) \r\n} \r\n\r\npr() |&gt;\r\n  pr_get(&quot;\/error&quot;, function() 1 + &quot;1&quot;) |&gt;\r\n  pr_set_error(handler_error) |&gt;\r\n  pr_run()\r\n<\/pre>\n<p>Now, if we request the \u201c\/error\u201d resource, we\u2019ll get the custom error message:<\/p>\n<div id=\"attachment_23608\" style=\"width: 964px\" class=\"wp-caption aligncenter\"><img loading=\"lazy\" aria-describedby=\"caption-attachment-23608\" decoding=\"async\" class=\"size-full wp-image-23608\" src=\"https:\/\/wordpress.appsilon.com\/wp-content\/uploads\/2024\/03\/img2-1.webp\" alt=\"Browser displaying a JSON error message and a network console with a 500 Internal Server Error.\" width=\"450\" \/><p id=\"caption-attachment-23608\" class=\"wp-caption-text\">Internal Server Error Displayed in Browser<\/p><\/div>\n<h3>Custom R Plumber Error Handling with <code>abort()<\/code> and <code>pr_set_error()<\/code><\/h3>\n<p>{plumber} will catch any error raised in the endpoints and pass the associated condition object to the error handler. This approach simplifies error management by providing a single place for all the logic related to error handling. By using classed conditions in the endpoints, developers can easily determine the most appropriate response for each type of error encountered.<\/p>\n<p>In the error handler, we can employ the <code>try_fetch()<\/code> function to get an elegant solution for picking the appropriate HTTP error status based on the class of the condition. This approach enables the propagation of metadata about the condition that caused the error from any point in the lifecycle of the request processing, facilitating a tidy and uniform way to return HTTP status error codes (e.g., 400, 429, 500, etc.).<\/p>\n<p><strong>Example exercise:<\/strong> Create an endpoint that generates a status code 400 if a request parameter is missing, add an error message that the issue is on the client side, and attach metadata to the HTTP response about the missing parameter.<\/p>\n<p>We can break down the answer into three parts:<\/p>\n<h3><strong>1. Creating a function that signals the error:<\/strong><\/h3>\n<pre>\r\nlibrary(plumber)\r\nlibrary(rlang)\r\n\r\nhi_function &lt;- function(name) {\r\n  if(is_missing(name)) {\r\n    abort(&quot;name parameter is missing&quot;, class = &quot;error_400&quot;)\r\n  }\r\n  paste(&quot;Hi&quot;, name)\r\n}\r\n<\/pre>\n<ul>\n<li>If the <code>name<\/code> parameter is missing; it aborts the request with a custom error message and class <code>error_400<\/code>.<\/li>\n<li>If the <code>name<\/code> parameter is provided, it concatenates \u201cHi\u201d with the provided <code>name<\/code> and returns the result.<\/li>\n<\/ul>\n<h3><strong>2. Creating a custom error handler:<\/strong><\/h3>\n<pre>\r\nhandler_error &lt;- function(req, res, err) {\r\n  # Transform vectors of length 1 to JSON strings\r\n  res$serializer &lt;- serializer_unboxed_json()\r\n\r\n  try_fetch({\r\n    cnd_signal(err)\r\n  }, error_400 = \\(cnd) {\r\n    res$status &lt;- 400\r\n    list(\r\n      error = &quot;Server cannot process the request due to a client error&quot;,\r\n      message = cnd_message(cnd)\r\n    )\r\n  }, error = \\(cnd) {\r\n    res$status &lt;- 500\r\n    list(\r\n      error = &quot;Oopsie: Internal Server Error&quot;\r\n    )\r\n  })\r\n}\r\n<\/pre>\n<ul>\n<li><code>cnd_signal()<\/code> signals the error condition; <code>try_fetch()<\/code> catches and passes it to the appropriate error handlers.<\/li>\n<li>If the error class is <code>error_400<\/code>, indicating a client error, the HTTP status code of the response is set to 400, and a JSON object containing an error message and the message associated with the error (<code>cnd_message(e)<\/code>) is returned.<\/li>\n<li>If the error class is unspecified (i.e., not caught by the <code>error_400<\/code> handler), indicating an internal server error, the HTTP status code of the response is set to 500, and a generic error message is returned.<\/li>\n<\/ul>\n<h3>3. Initialising plumber API:<\/h3>\n<pre>\r\npr() |&gt;\r\n  pr_get(&quot;\/hi&quot;, hi_function) |&gt;\r\n  pr_set_error(handler_error) |&gt;\r\n  pr_run()\r\n<\/pre>\n<ul>\n<li>This sequence of chained function calls sets up the Plumber router (<code>pr()<\/code>), defines a GET endpoint at \u201c\/hi\u201d that is handled by the <code>hi_function<\/code>, and sets the error handler for the router to <code>handler_error<\/code>.<\/li>\n<li>Requests to the \u201c\/hi\u201d endpoint are processed by <code>hi_function<\/code>, and any errors that occur during request processing are handled by <code>handler_error<\/code>.<\/li>\n<\/ul>\n<p>Now if we make a GET request without providing the <code>name<\/code> parameter that is required, we will get a 400 status code followed by an informative message.<\/p>\n<div id=\"attachment_23616\" style=\"width: 989px\" class=\"wp-caption aligncenter\"><img loading=\"lazy\" aria-describedby=\"caption-attachment-23616\" decoding=\"async\" class=\"size-full wp-image-23616\" src=\"https:\/\/wordpress.appsilon.com\/wp-content\/uploads\/2024\/03\/img3-1.webp\" alt=\"\"A web browser showing a 400 Bad Request error with a JSON response message that states 'Server cannot process the request due to a client error', and a detailed message 'name parameter is missing'\" width=\"450\" \/><p id=\"caption-attachment-23616\" class=\"wp-caption-text\">400 Bad Request Error for Missing Parameter in Web Browser<\/p><\/div>\n<p>By utilizing this pattern, we can construct error handlers tailored to various HTTP status codes, such as 400 for client errors, 429 for rate limit exceeded errors, and 500 for internal server errors, allowing for comprehensive error management in our API endpoints. This approach enables precise control over error responses, ensuring the appropriate status code and message are returned to clients based on the encountered condition.<\/p>\n<hr \/>\n<h2 id=\"summarizing-api-oopsies-101\">Summing up R Plumber Custom Error Messages<\/h2>\n<p>In conclusion, crafting solid APIs that return meaningful status codes is essential for enhancing user experience and facilitating effective communication between clients and servers.<\/p>\n<p>By adhering to the R way of conditions, developers can implement robust error-handling mechanisms that seamlessly integrate with their APIs. Leveraging the flexibility of custom error classes and precise condition signaling, the R programming language provides a powerful framework for constructing APIs that deliver clear and informative error responses, ultimately fostering trust and reliability in application interactions.<\/p>\n<blockquote><p>Did you find this useful? <a href=\"https:\/\/www.shinyconf.com\/\" rel=\"nofollow\" target=\"_blank\">Join Alexandros and a community of innovative R\/Shiny developers at ShinyConf 2024. Don\u2019t miss out on early bird registration \u2013 reserve your spot today!<\/a><\/p><\/blockquote>\n<p>The post appeared first on appsilon.com\/blog\/.<\/p>\n\n<div style=\"border: 1px solid; background: none repeat scroll 0 0 #EDEDED; margin: 1px; font-size: 13px;\">\r\n<div style=\"text-align: center;\">To <strong>leave a comment<\/strong> for the author, please follow the link and comment on their blog: <strong><a href=\"https:\/\/appsilon.com\/api-oopsies-101\/\"> Tag: r - Appsilon | Enterprise R Shiny Dashboards<\/a><\/strong>.<\/div>\r\n<hr \/>\r\n<a href=\"https:\/\/www.r-bloggers.com\/\" rel=\"nofollow\">R-bloggers.com<\/a> offers <strong><a href=\"https:\/\/feedburner.google.com\/fb\/a\/mailverify?uri=RBloggers\" rel=\"nofollow\">daily e-mail updates<\/a><\/strong> about <a title=\"The R Project for Statistical Computing\" href=\"https:\/\/www.r-project.org\/\" rel=\"nofollow\">R<\/a> news and tutorials about <a title=\"R tutorials\" href=\"https:\/\/www.r-bloggers.com\/how-to-learn-r-2\/\" rel=\"nofollow\">learning R<\/a> and many other topics. <a title=\"Data science jobs\" href=\"https:\/\/www.r-users.com\/\" rel=\"nofollow\">Click here if you're looking to post or find an R\/data-science job<\/a>.\r\n\r\n<hr>Want to share your content on R-bloggers?<a href=\"https:\/\/www.r-bloggers.com\/add-your-blog\/\" rel=\"nofollow\"> click here<\/a> if you have a blog, or <a href=\"http:\/\/r-posts.com\/\" rel=\"nofollow\"> here<\/a> if you don't.\r\n<\/div>","protected":false},"excerpt":{"rendered":"<div style = \"width:60%; display: inline-block; float:left; \">\nAPIs (Application Programming Interfaces) are the backbone of modern software development, enabling seamless communication between different applications or components. However, like any technology, APIs are not immune to errors. In this brief guide, we\u2019ll explore some common HTTP status codes and shed light on this crucial aspect of web &#8230;<\/div>\n<div style = \"width: 40%; display: inline-block; float:right;\"><\/div>\n<div style=\"clear: both;\"><\/div>\n","protected":false},"author":2446,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":[],"categories":[4],"tags":[],"aioseo_notices":[],"jetpack-related-posts":[],"amp_enabled":true,"_links":{"self":[{"href":"https:\/\/www.r-bloggers.com\/wp-json\/wp\/v2\/posts\/382514"}],"collection":[{"href":"https:\/\/www.r-bloggers.com\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.r-bloggers.com\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.r-bloggers.com\/wp-json\/wp\/v2\/users\/2446"}],"replies":[{"embeddable":true,"href":"https:\/\/www.r-bloggers.com\/wp-json\/wp\/v2\/comments?post=382514"}],"version-history":[{"count":2,"href":"https:\/\/www.r-bloggers.com\/wp-json\/wp\/v2\/posts\/382514\/revisions"}],"predecessor-version":[{"id":382629,"href":"https:\/\/www.r-bloggers.com\/wp-json\/wp\/v2\/posts\/382514\/revisions\/382629"}],"wp:attachment":[{"href":"https:\/\/www.r-bloggers.com\/wp-json\/wp\/v2\/media?parent=382514"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.r-bloggers.com\/wp-json\/wp\/v2\/categories?post=382514"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.r-bloggers.com\/wp-json\/wp\/v2\/tags?post=382514"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}