{"id":2574,"date":"2021-01-08T12:42:41","date_gmt":"2021-01-08T12:42:41","guid":{"rendered":"https:\/\/ntspl.co.in\/blog\/?p=2574"},"modified":"2021-12-20T11:20:53","modified_gmt":"2021-12-20T11:20:53","slug":"best-practices-for-building-offline-apps","status":"publish","type":"post","link":"https:\/\/www.ntspl.co.in\/blog\/best-practices-for-building-offline-apps\/","title":{"rendered":"Best Practices for Building Offline Apps"},"content":{"rendered":"<p>It\u2019s a hard truth every software developer faces at some point: consistent Internet access is never guaranteed. WiFi is available everywhere nowadays, but service can be spotty or overloaded with connection requests (such as at large events or conferences). And, you can\u2019t stop your users from accessing your app while their connection is poor or non-existent. So as a developer, what do you do?\u00a0<em>Embrace it.<\/em>\u00a0Tame any concerns about building offline experiences by learning the fundamentals of Offline First.<\/p>\n<p><a href=\"http:\/\/offlinefirst.org\/\">Offline First<\/a>\u00a0is the software engineering principle that assumes that apps can and will be used without a network connection at some point. This is especially important if you are designing for a mobile audience, who may go offline multiple times per day. Building Offline First apps increase the utility of your app and lead to better user satisfaction.<\/p>\n<h2>Offline First Mindset<\/h2>\n<p>Here are some of the things you can do to adopt an Offline First mindset.<\/p>\n<h3>Cache early, cache often<\/h3>\n<p>As the old saying goes, \u201cthe early bird\u00a0<em>caches<\/em> the worm.\u201d<\/p>\n<p>Bad jokes aside,\u00a0<a href=\"https:\/\/developers.google.com\/web\/ilt\/pwa\/caching-files-with-service-worker\">cache your content<\/a>\u00a0early and often while connections are stable to save yourself headaches when network connections are more intermittent.<\/p>\n<p>Caching content improves app performance since the now-local information is retrieved faster. Apps can be opened and closed while keeping their state that way as well. Check out some specific strategies for caching in the section on\u00a0<em>Service Workers<\/em>\u00a0below.<\/p>\n<h3>Identify the Non-Negotiable<\/h3>\n<p>On the flip side, some features are non-negotiable: you simply have to be connected to the internet to use them, such as location services.<\/p>\n<p>That\u2019s OK! Identify what features need an internet connection, then create messages or alerts informing those features\u2019 needs to users. Users will appreciate it because you\u2019ll take the guesswork out of understanding your app\u2019s limits when offline.<\/p>\n<h3>Be Ready for Conflict<\/h3>\n<p>Conflicting information can be a big problem. It may not be enough to simply merge and override all changes except the last time a user was online. Users should have a clear understanding of what happens when conflict is unavoidable. Once they know their options, they can choose what works for them.<\/p>\n<p>There are several solutions for handling conflicting information. It is up to you if you want the user to pick and choose which version to keep or to institute a \u201clast write wins\u201d rule. No matter what you choose, the app should handle this gracefully.<\/p>\n<h3>Know When to Apply Updates<\/h3>\n<p>Update too frequently and you may miss out on the benefits of being offline first. Update too slowly and you may create more conflicts than necessary and frustrate users.<\/p>\n<p>Knowing\u00a0<em>what<\/em>\u00a0to update along with\u00a0<em>when<\/em>\u00a0to update is an important consideration as well. Uploading only the most important data to your servers saves on storage and the time needed to upload.<\/p>\n<h3>Expect the Unexpected<\/h3>\n<p>Users are unpredictable. While you should test for a wide variety of situations you cannot account for everything. Track what users actually do with continued testing and surveying when users are offline.<\/p>\n<p>One personal example is my wife borrowing e-books from the library to read on her Kindle. Since borrowed books \u201cexpire\u201d after a set rental period, she keeps her Kindle offline as long as possible to ensure that if she needs more time to finish a book, she can do so without losing access to it.<\/p>\n<p>I do not know if the Kindle App\u2019s developers anticipated people hanging onto books like that, but it is one example of people remaining offline as long as it takes to fit their use cases. The idea that everyone wants to constantly be online is one that should be tested against your own assumptions.<\/p>\n<p>Those are the concepts you need to be aware of. To put these concepts to work you\u2019ll need the right tools. Fortunately, there are a number of existing tools that make it easier for developers to program with Offline First in mind.<\/p>\n<h2>Tools<\/h2>\n<h3>The Network Information API<\/h3>\n<p>The\u00a0<a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/API\/Network_Information_API\">Network Information API<\/a>\u00a0is a critical tool for developers who want to ensure a smooth experience for users. Developers can use this API to create paths that open an app in online or offline mode from the moment a user opens the app. This cuts down on the errors a user sees when an app tries to find a connection but fails to open at all.<\/p>\n<p>The Network Information API even distinguishes between WiFi and Cellular networks, allowing for fine-grained control over how content is served. The API also detects changes that allow apps to continue to function as a user moves from one network connection to another (like going in and out of subway tunnels).<\/p>\n<p>As an example, when a user is on a slower connection, they get an alert that performance may be degraded:<\/p>\n<pre><code class=\"language-javascript hljs\"><span class=\"hljs-keyword\">if<\/span> (navigator.connection.effectiveType != <span class=\"hljs-string\">'4g'<\/span>) {\r\n  <span class=\"hljs-built_in\">console<\/span>.log(<span class=\"hljs-string\">'slow connection!'<\/span>);\r\n  <span class=\"hljs-comment\">\/\/ show modal dialog warning user that video will be loaded at lower resolution<\/span>\r\n}\r\n<span class=\"hljs-keyword\">else<\/span> {\r\n  <span class=\"hljs-built_in\">console<\/span>.log(<span class=\"hljs-string\">'ready to go!'<\/span>);\r\n  <span class=\"hljs-comment\">\/\/ immediate load video file<\/span>\r\n}\r\n<\/code><\/pre>\n<p>Another option is Capacitor\u2019s\u00a0<a href=\"https:\/\/capacitorjs.com\/docs\/apis\/network\">Network API<\/a>. It extends the Network Information API to provide even more useful features for web and mobile apps, such as monitoring the network for status changes, which your app can then react to.<\/p>\n<pre><code class=\"language-javascript hljs\"><span class=\"hljs-keyword\">import<\/span> { Plugins } <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">'@capacitor\/core'<\/span>;\r\n\r\n<span class=\"hljs-keyword\">const<\/span> { Network } = Plugins;\r\n\r\n<span class=\"hljs-keyword\">let<\/span> handler = Network.addListener(<span class=\"hljs-string\">'networkStatusChange'<\/span>, <span class=\"hljs-function\">(<span class=\"hljs-params\">status<\/span>) =&gt;<\/span> {\r\n  <span class=\"hljs-built_in\">console<\/span>.log(<span class=\"hljs-string\">\"Network status changed\"<\/span>, status);\r\n});\r\n\r\n<span class=\"hljs-comment\">\/\/ Get the current network status<\/span>\r\n<span class=\"hljs-keyword\">let<\/span> status = <span class=\"hljs-keyword\">await<\/span> Network.getStatus();\r\n\r\n<span class=\"hljs-comment\">\/\/ Example output:<\/span>\r\n{\r\n  <span class=\"hljs-string\">\"connected\"<\/span>: <span class=\"hljs-literal\">true<\/span>,\r\n  <span class=\"hljs-string\">\"connectionType\"<\/span>: <span class=\"hljs-string\">\"wifi\"<\/span>\r\n}\r\n<\/code><\/pre>\n<p>Monitoring the network with these two APIs is a crucial early step in making your offline experiences as smooth as possible.<\/p>\n<h3>Service Workers<\/h3>\n<p><a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/API\/Service_Worker_API\/Using_Service_Workers\">Service Workers<\/a>\u00a0let you control network requests, cache those requests, and then allow users to access cached content when offline.<\/p>\n<p>With the Cache API, it is simple to add resources with the built-in\u00a0<em>add<\/em>\u00a0method. For example:<\/p>\n<pre><code class=\"language-javascript hljs\"><span class=\"hljs-keyword\">const<\/span> resource = caches.add(<span class=\"hljs-string\">'\/styles.css'<\/span>);\r\n<\/code><\/pre>\n<p>or you can use\u00a0<em>addAll<\/em>\u00a0to add multiple files, which uses a promise to ensure all the resources are cached and rejects the request if one is not loaded. For example:<\/p>\n<pre><code class=\"language-javascript hljs\"><span class=\"hljs-keyword\">const<\/span> resources = caches.addAll(<span class=\"hljs-string\">'\/styles.css'<\/span>, <span class=\"hljs-string\">'\/fonts.woff'<\/span>, <span class=\"hljs-string\">'otherdata\/'<\/span>); \r\n<\/code><\/pre>\n<p>An event listener can fire after the app is installed which creates a cache and loads its contents ready to be used once installation is complete:<\/p>\n<pre><code class=\"language-javascript hljs\">self.addEventListener(<span class=\"hljs-string\">'install'<\/span>, <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span>(<span class=\"hljs-params\">event<\/span>) <\/span>{\r\n  event.waitUntil(caches.open(<span class=\"hljs-string\">'static-files'<\/span>)\r\n    .then(<span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span>(<span class=\"hljs-params\">cache<\/span>) <\/span>{\r\n       <span class=\"hljs-keyword\">return<\/span> caches.addAll([\r\n         <span class=\"hljs-string\">'\/css\/styles.css'<\/span>,\r\n         <span class=\"hljs-string\">'\/static\/images\/'<\/span>,\r\n         <span class=\"hljs-string\">'\/fonts\/fonts.woff'<\/span>\r\n       ]);\r\n    })\r\n  );\r\n }\r\n);\r\n<\/code><\/pre>\n<p>From there you can create additional functions that check for updates to the cached contents, delete out of date files, or add additional files as needed.<\/p>\n<h2>Offline Storage<\/h2>\n<p>How you save data locally is another important consideration. There are several solutions: localStorage, IndexedDB, and Ionic Offline Storage.<\/p>\n<h3>localStorage<\/h3>\n<p><a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/API\/Window\/localStorage\">localStorage<\/a>\u00a0contains built-in methods to create and store key -&gt; value pairs. These are persisted offline and when a browser is closed. You can declare and retrieve data using\u00a0<code>setItem<\/code>\u00a0and\u00a0<code>getItem<\/code>. The following code sets a \u2018name\u2019 value and returns it when called.<\/p>\n<pre><code class=\"language-javascript hljs\"><span class=\"hljs-keyword\">var<\/span> offlineStore = <span class=\"hljs-built_in\">window<\/span>.localStorage;\r\nofflineStore.setItem(<span class=\"hljs-string\">'name'<\/span>, <span class=\"hljs-string\">'John'<\/span>);\r\n<span class=\"hljs-keyword\">var<\/span> name = <span class=\"hljs-built_in\">localStorage<\/span>.getItem(<span class=\"hljs-string\">'name'<\/span>);\r\n<\/code><\/pre>\n<p>Use the\u00a0<code>clear<\/code>\u00a0method to delete data.<\/p>\n<pre><code class=\"language-javascript hljs\"> <span class=\"hljs-built_in\">localStorage<\/span>.clear(<span class=\"hljs-string\">'name'<\/span>);\r\n<\/code><\/pre>\n<h3>IndexedDB<\/h3>\n<p>localStorage is great for storing strings, but what if you want to store more complex data like images or blobs? That\u2019s where\u00a0<a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/API\/IndexedDB_API\">IndexedDB<\/a>\u00a0comes in. IndexedDB lets you create a new local database connection and map objects to the schema you provide. It requires more code than localStorage but you can take advantage of more features.<\/p>\n<p>When creating an IndexedDB you create a \u201cstore\u201d for your data and IndexedDB lets you go from there. Here\u2019s an example of creating an IndexedDB of books and their authors:<\/p>\n<pre><code class=\"language-javascript hljs\"><span class=\"hljs-comment\">\/\/ creates a new version of the database if needed<\/span>\r\n<span class=\"hljs-keyword\">const<\/span> dbName = <span class=\"hljs-string\">\"books_db\"<\/span>;\r\n<span class=\"hljs-keyword\">let<\/span> request = <span class=\"hljs-built_in\">window<\/span>.indexedDB.open(dbName, <span class=\"hljs-number\">1<\/span>),db,tx,store, index;\r\n\r\nrequest.onupgradeneeded = <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span>() <\/span>{\r\n  <span class=\"hljs-keyword\">let<\/span> db = request.result,\r\n  store = db.createObjectStore(<span class=\"hljs-string\">\"booksStore\"<\/span>, { <span class=\"hljs-attr\">keyPath<\/span>: <span class=\"hljs-string\">\"isbn\"<\/span> }), \r\n  index = store.createIndex(<span class=\"hljs-string\">\"title\"<\/span>, <span class=\"hljs-string\">\"title\"<\/span>);\r\n} \r\n\r\nrequest.onsuccess = <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span>() <\/span>{\r\n  <span class=\"hljs-comment\">\/\/ db where data goes<\/span>\r\n  db = request.result;\r\n\r\n  <span class=\"hljs-comment\">\/\/ defines the transactions allowed<\/span>\r\n  tx = db.transaction(<span class=\"hljs-string\">\"booksStore\"<\/span>, <span class=\"hljs-string\">\"readwrite\"<\/span>);\r\n\r\n  <span class=\"hljs-comment\">\/\/ the store you create for the data that uses the 'isbn' key.<\/span>\r\n  store = tx.objectStore(<span class=\"hljs-string\">\"booksStore\"<\/span>);\r\n\r\n  <span class=\"hljs-comment\">\/\/ an index you can use to search data<\/span>\r\n  index = store.index(<span class=\"hljs-string\">\"title\"<\/span>); \r\n\r\n  <span class=\"hljs-comment\">\/\/ insert data<\/span>\r\n  store.put({<span class=\"hljs-attr\">isbn<\/span>: <span class=\"hljs-number\">1234<\/span>, <span class=\"hljs-attr\">title<\/span>: <span class=\"hljs-string\">\"Moby Dick\"<\/span>, <span class=\"hljs-attr\">author<\/span>: <span class=\"hljs-string\">\"Herman Melville\"<\/span>});\r\n  store.put({<span class=\"hljs-attr\">isbn<\/span>: <span class=\"hljs-number\">4321<\/span>, <span class=\"hljs-attr\">title<\/span>: <span class=\"hljs-string\">\"Emma\"<\/span>, <span class=\"hljs-attr\">author<\/span>: <span class=\"hljs-string\">\"Jane Austen\"<\/span>}); \r\n\r\n  <span class=\"hljs-comment\">\/\/ retrieve data<\/span>\r\n  <span class=\"hljs-keyword\">let<\/span> book1 = store.get(<span class=\"hljs-number\">1234<\/span>);\r\n  book1.onsuccess = <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span>() <\/span>{\r\n    <span class=\"hljs-built_in\">console<\/span>.log(book1.result);\r\n  } \r\n\r\n  <span class=\"hljs-comment\">\/\/ close the database connection once the transaction has completed<\/span>\r\n  tx.oncomplete = <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span>() <\/span>{\r\n    db.close();\r\n  };\r\n};\r\n<\/code><\/pre>\n<p>You can learn more by reading the\u00a0<a href=\"https:\/\/www.w3.org\/TR\/IndexedDB\/#introduction\">IndexedDB docs<\/a>. There are other solutions like\u00a0<a href=\"https:\/\/github.com\/localForage\/localForage\">localForage<\/a>\u00a0which is a wrapper API for IndexedDB that aims to make it easier to work with.<\/p>\n<h3>Ionic\u2019s Offline Storage solution<\/h3>\n<p>For more advanced, robust data storage needs, check out Ionic\u2019s\u00a0<a href=\"https:\/\/ionic.io\/products\/offline-storage\">Offline Storage<\/a>\u00a0solution. It\u2019s a cross-platform data storage system that works on iOS and Android and powered by SQLite (a SQL database engine). Since it provides a performance-optimized query engine and on-device data encryption (256-bit AES), it\u2019s great for data-driven apps.<\/p>\n<p>Since it\u2019s based on the industry-standard SQL, it\u2019s easy for developers to add to their project. For example, creating a new table then inserting initial data:<\/p>\n<pre><code class=\"language-javascript hljs\"><span class=\"hljs-built_in\">this<\/span>.database.transaction(<span class=\"hljs-function\">(<span class=\"hljs-params\">tx<\/span>) =&gt;<\/span> {\r\n  tx.executeSql(<span class=\"hljs-string\">'CREATE TABLE IF NOT EXISTS software (name, company)'<\/span>);\r\n  tx.executeSql(<span class=\"hljs-string\">'INSERT INTO software VALUES (?,?)'<\/span>, [<span class=\"hljs-string\">'offline'<\/span>, <span class=\"hljs-string\">\"ionic\"<\/span>]);\r\n  tx.executeSql(<span class=\"hljs-string\">'INSERT INTO software VALUES (?,?)'<\/span>, [<span class=\"hljs-string\">'auth-connect'<\/span>, <span class=\"hljs-string\">\"ionic\"<\/span>]);\r\n});\r\n<\/code><\/pre>\n<p>Querying data involves writing SELECT statements then looping through the data results:<\/p>\n<pre><code class=\"language-javascript hljs\"><span class=\"hljs-built_in\">this<\/span>.database.transaction(<span class=\"hljs-function\">(<span class=\"hljs-params\">tx<\/span>) =&gt;<\/span> {\r\n  tx.executeSql(<span class=\"hljs-string\">\"SELECT * from software\"<\/span>, [], <span class=\"hljs-function\">(<span class=\"hljs-params\">tx, result<\/span>) =&gt;<\/span> {\r\n    <span class=\"hljs-comment\">\/\/ Rows is an array of results. Use zero-based indexing to access<\/span>\r\n    <span class=\"hljs-comment\">\/\/ each element in the result set: item(0), item(1), etc. <\/span>\r\n    <span class=\"hljs-keyword\">for<\/span> (<span class=\"hljs-keyword\">let<\/span> i = <span class=\"hljs-number\">0<\/span>; i &lt; result.rows.length; i++) {\r\n      <span class=\"hljs-comment\">\/\/ { name: \"offline-storage\", company: \"ionic\", type: \"native\", version: \"2.0\" }<\/span>\r\n      <span class=\"hljs-built_in\">console<\/span>.log(result.rows.item(i));\r\n\r\n      <span class=\"hljs-comment\">\/\/ ionic<\/span>\r\n      <span class=\"hljs-built_in\">console<\/span>.log(result.rows.item(i).company);\r\n    }\r\n  });\r\n});\r\n<\/code><\/pre>\n<p>Learn more about\u00a0<a href=\"https:\/\/ionic.io\/products\/offline-storage\">building fast, data-driven apps here<\/a>.<\/p>\n<h3>Availability Messages<\/h3>\n<p>It\u2019s important to communicate with users when their offline experience changes. Your app should be proactive about letting users know when they are online or offline. Use network event listeners to detect changes in a user\u2019s connection and send messages letting a user know that not all features may be available.<\/p>\n<p>The\u00a0<a href=\"https:\/\/ionicframework.com\/docs\/api\/toast\">Ionic Framework Toast Component<\/a>\u00a0is one tool that can help you set availability messages quickly and easily. Once your app detects that it is offline use a toast to notify the user:<\/p>\n<pre><code class=\"language-javascript hljs\"><span class=\"hljs-comment\">\/\/ Display a toast message if the network connection goes offline<\/span>\r\n<span class=\"hljs-keyword\">async<\/span> <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">showOfflineToast<\/span>() <\/span>{  \r\n  <span class=\"hljs-keyword\">const<\/span> toast = <span class=\"hljs-built_in\">document<\/span>.createElement(<span class=\"hljs-string\">'ion-toast'<\/span>);  \r\n\r\n  <span class=\"hljs-comment\">\/\/ Display the message that the User is now offline. <\/span>\r\n  toast.message = <span class=\"hljs-string\">'The latest chat messages will appear when you are back online.'<\/span>;\r\n\r\n  <span class=\"hljs-comment\">\/\/ Display the toast message for 2 seconds<\/span>\r\n  toast.duration = <span class=\"hljs-number\">2000<\/span>;\r\n}\r\n<\/code><\/pre>\n<h2>Start Building Offline Apps<\/h2>\n<p>Users will go offline at some point, due to intermittent network connectivity or their location (remote areas or thick-walled buildings for example). They may simply be concerned about limiting bandwidth usage to save on costs, preferring offline modes over the need for constant connection. Regardless of the reason, we must account for these scenarios.<\/p>\n<p>If this seems challenging to get started with Offline First, don\u2019t worry. Use the following checklist to ensure that you are on the right track when developing an Offline-First experience. Your users will thank you!<\/p>\n<ol>\n<li>Are you designing for an Offline-First experience?<\/li>\n<li>Are you checking for network connections using the Network API? What type of connections are you detecting?<\/li>\n<li>Have you configured a service worker to serve offline content?<\/li>\n<li>What features cannot work without an internet connection? How will you let users know what is or is not possible offline?<\/li>\n<li>What are your storage solutions for offline content? Will you rely on localStorage or a more robust system like IndexedDB?<\/li>\n<\/ol>\n<p>The post <a href=\"https:\/\/ionicframework.com\/blog\/best-practices-for-building-offline-apps\/\" rel=\"nofollow\">Best Practices for Building Offline Apps<\/a> appeared first on <a href=\"https:\/\/ionicframework.com\/blog\" rel=\"nofollow\">Ionic Blog<\/a>.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>It\u2019s a hard truth every software developer faces at some point: consistent Internet access is never guaranteed. WiFi is available everywhere nowadays, but service can be spotty or overloaded with connection requests (such as at large events or conferences). And, you can\u2019t stop your users from accessing your app while their connection is poor or [&hellip;]<\/p>\n","protected":false},"author":53,"featured_media":2604,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[11],"tags":[456,455],"class_list":["post-2574","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-development","tag-building-offline-apps","tag-offline-first-apps"],"acf":{"custom_meta_title":"Best Practices for Building Offline First Apps | NTSPL","meta_description":"Consistent internet connection is never guaranteed. Let's discuss the approaches that will help you to  build offline first apps with a better user experience.","meta_keyword":"building offline apps, offline first app","other_meta_tag":"<meta property=og:locale content=\"en-IN\" \/>\r\n<meta property=og:type content=\"website\" \/>\r\n<meta property=og:title content=\"Best Practices for Building Offline First Apps | NTSPL\"\/>\r\n<meta property=og:description content=\"Consistent internet connection is never guaranteed. Let's discuss the approaches that will help you to  build offline first apps with a better user experience.\"\/>\r\n<meta property=\"og:image\" content=\"https:\/\/ntspl.co.in\/blog\/wp-content\/uploads\/2021\/01\/building-offline-first-app.jpg\"\/>\r\n<meta property=og:url content=\"https:\/\/www.ntspl.co.in\/blog\/best-practices-for-building-offline-apps\"\/>\r\n<meta property=og:site_name content=NTSPL \/>\r\n<meta name=\"twitter:site\" content=\"@NTSPL\">\r\n<meta name=twitter:card content=\"summary\" \/>\r\n<meta name=twitter:description content=\"Consistent internet connection is never guaranteed. Let's discuss the approaches that will help you to  build offline first apps with a better user experience.\"\/>\r\n<meta name=twitter:title content=\"Best Practices for Building Offline First Apps | NTSPL\"\/>"},"_links":{"self":[{"href":"https:\/\/www.ntspl.co.in\/blog\/wp-json\/wp\/v2\/posts\/2574"}],"collection":[{"href":"https:\/\/www.ntspl.co.in\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.ntspl.co.in\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.ntspl.co.in\/blog\/wp-json\/wp\/v2\/users\/53"}],"replies":[{"embeddable":true,"href":"https:\/\/www.ntspl.co.in\/blog\/wp-json\/wp\/v2\/comments?post=2574"}],"version-history":[{"count":6,"href":"https:\/\/www.ntspl.co.in\/blog\/wp-json\/wp\/v2\/posts\/2574\/revisions"}],"predecessor-version":[{"id":4408,"href":"https:\/\/www.ntspl.co.in\/blog\/wp-json\/wp\/v2\/posts\/2574\/revisions\/4408"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.ntspl.co.in\/blog\/wp-json\/wp\/v2\/media\/2604"}],"wp:attachment":[{"href":"https:\/\/www.ntspl.co.in\/blog\/wp-json\/wp\/v2\/media?parent=2574"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.ntspl.co.in\/blog\/wp-json\/wp\/v2\/categories?post=2574"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.ntspl.co.in\/blog\/wp-json\/wp\/v2\/tags?post=2574"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}