{"id":2864,"date":"2026-10-21T10:31:47","date_gmt":"2026-10-21T08:31:47","guid":{"rendered":"https:\/\/extendsclass.com\/blog\/?p=2864"},"modified":"2026-10-21T10:23:28","modified_gmt":"2026-10-21T08:23:28","slug":"designing-a-database-schema-for-real-time-inventory-tracking","status":"publish","type":"post","link":"https:\/\/extendsclass.com\/blog\/designing-a-database-schema-for-real-time-inventory-tracking","title":{"rendered":"Designing a database schema for real-time inventory tracking"},"content":{"rendered":"\n<p class=\"wp-block-paragraph\">A warehouse can have 40 units of a product on its shelves while the application says 43 are available. Perhaps a return was recorded twice. Perhaps an order reserved stock without updating the balance correctly. Or two requests reached the database almost simultaneously, and both were approved.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Reliable inventory tracking starts with separating product details from the quantities held at each warehouse.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">By the time anyone spots the mismatch, the warehouse may already be trying to fulfill another order. Tracking down the cause can mean checking transaction logs, reservation records and the rules governing stock changes. Sometimes the problem is a missing constraint; sometimes the schema simply doesn&#8217;t reflect what happens on the warehouse floor. That is the sort of detail that needs sorting out when building <a href=\"https:\/\/sysgears.com\/solution\/inventory-management-software\/\"><u>custom inventory software<\/u><\/a>, not after the wrong balance has started disrupting orders.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Start with the rules the business follows. The database should enforce the rules that affect correctness, rather than leaving every application screen to interpret them independently.<\/p>\n\n\n\n<div id=\"ez-toc-container\" class=\"ez-toc-v2_0_47_1 counter-hierarchy ez-toc-counter ez-toc-grey ez-toc-container-direction\">\n<div class=\"ez-toc-title-container\">\n<p class=\"ez-toc-title\">Table of Contents<\/p>\n<span class=\"ez-toc-title-toggle\"><a href=\"#\" class=\"ez-toc-pull-right ez-toc-btn ez-toc-btn-xs ez-toc-btn-default ez-toc-toggle\" aria-label=\"ez-toc-toggle-icon-1\"><label for=\"item-6ada4950b9cf8\" aria-label=\"Table of Content\"><span style=\"display: flex;align-items: center;width: 35px;height: 30px;justify-content: center;direction:ltr;\"><svg style=\"fill: #999;color:#999\" xmlns=\"http:\/\/www.w3.org\/2000\/svg\" class=\"list-377408\" width=\"20px\" height=\"20px\" viewBox=\"0 0 24 24\" fill=\"none\"><path d=\"M6 6H4v2h2V6zm14 0H8v2h12V6zM4 11h2v2H4v-2zm16 0H8v2h12v-2zM4 16h2v2H4v-2zm16 0H8v2h12v-2z\" fill=\"currentColor\"><\/path><\/svg><svg style=\"fill: #999;color:#999\" class=\"arrow-unsorted-368013\" xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"10px\" height=\"10px\" viewBox=\"0 0 24 24\" version=\"1.2\" baseProfile=\"tiny\"><path d=\"M18.2 9.3l-6.2-6.3-6.2 6.3c-.2.2-.3.4-.3.7s.1.5.3.7c.2.2.4.3.7.3h11c.3 0 .5-.1.7-.3.2-.2.3-.5.3-.7s-.1-.5-.3-.7zM5.8 14.7l6.2 6.3 6.2-6.3c.2-.2.3-.5.3-.7s-.1-.5-.3-.7c-.2-.2-.4-.3-.7-.3h-11c-.3 0-.5.1-.7.3-.2.2-.3.5-.3.7s.1.5.3.7z\"\/><\/svg><\/span><\/label><input  type=\"checkbox\" id=\"item-6ada4950b9cf8\"><\/a><\/span><\/div>\n<nav><ul class='ez-toc-list ez-toc-list-level-1 ' ><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-1\" href=\"https:\/\/extendsclass.com\/blog\/designing-a-database-schema-for-real-time-inventory-tracking\/#Separate_products_from_warehouse_balances\" title=\"Separate products from warehouse balances\">Separate products from warehouse balances<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-2\" href=\"https:\/\/extendsclass.com\/blog\/designing-a-database-schema-for-real-time-inventory-tracking\/#Give_each_stockable_item_a_reliable_identity\" title=\"Give each stockable item a reliable identity\">Give each stockable item a reliable identity<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-3\" href=\"https:\/\/extendsclass.com\/blog\/designing-a-database-schema-for-real-time-inventory-tracking\/#Keep_the_trail_of_stock_movements\" title=\"Keep the trail of stock movements\">Keep the trail of stock movements<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-4\" href=\"https:\/\/extendsclass.com\/blog\/designing-a-database-schema-for-real-time-inventory-tracking\/#Available_stock_is_a_business_rule\" title=\"Available stock is a business rule\">Available stock is a business rule<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-5\" href=\"https:\/\/extendsclass.com\/blog\/designing-a-database-schema-for-real-time-inventory-tracking\/#When_two_orders_claim_the_same_stock\" title=\"When two orders claim the same stock\">When two orders claim the same stock<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-6\" href=\"https:\/\/extendsclass.com\/blog\/designing-a-database-schema-for-real-time-inventory-tracking\/#Getting_changes_onto_the_screen\" title=\"Getting changes onto the screen\">Getting changes onto the screen<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-7\" href=\"https:\/\/extendsclass.com\/blog\/designing-a-database-schema-for-real-time-inventory-tracking\/#Data_types_and_constraints\" title=\"Data types and constraints\">Data types and constraints<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-8\" href=\"https:\/\/extendsclass.com\/blog\/designing-a-database-schema-for-real-time-inventory-tracking\/#Test_what_happens_when_things_go_wrong\" title=\"Test what happens when things go wrong\">Test what happens when things go wrong<\/a><\/li><\/ul><\/nav><\/div>\n<h2 class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"Separate_products_from_warehouse_balances\"><\/span>Separate products from warehouse balances<span class=\"ez-toc-section-end\"><\/span><\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">A product catalog tells the application what a business sells. It doesn&#8217;t tell the warehouse how many units are sitting on a shelf in Lviv, how many are in Kyiv, or whether the last box has already been promised to a customer. Those details belong elsewhere in the database schema.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Take a stainless steel bottle stocked in three warehouses. There is no need to copy its name, description, and other catalog details into three separate product records. Keep one entry in products, store the warehouse locations in warehouses, and use an inventory table to record the quantity held at each location. The arrangement is fairly simple, but it saves the application from mixing up product information and physical stock as the business grows.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">A duplicate balance can slip in when an import runs twice or a piece of code inserts a new row instead of updating the old one. Give the database a way to reject that: for a product stored at a single warehouse, the pair product_id and warehouse_id should be unique. That rule changes once stock is split across bins or batches, since several rows for the same product and warehouse may be perfectly valid.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">A total quantity doesn&#8217;t tell the whole story when some goods can&#8217;t be sold. Stock awaiting inspection, damaged items, and units already reserved for orders may all be sitting in the warehouse, but they&#8217;re in different states. How those states are stored depends on the operation. The important thing is to keep the meaning of each quantity clear, rather than letting one balance stand for stock that isn&#8217;t actually available.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"Give_each_stockable_item_a_reliable_identity\"><\/span>Give each stockable item a reliable identity<span class=\"ez-toc-section-end\"><\/span><\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">The awkward cases in SKU management tend to be the products that look alike. A 500 ml bottle and its one-liter counterpart may have the same name apart from the size, yet a picker can&#8217;t send one in place of the other without risking a wrong order. Different colors, pack sizes, or configurations create the same issue whenever customers buy them as distinct items.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Give each stockable variant its own SKU. The catalog can still group related items under a parent product, but the records used for ordering and inventory need to distinguish the variants. Otherwise, stock intended for one item can quietly be counted against another.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">An internal product ID makes a suitable primary key. The SKU can have a separate unique constraint, allowing the business to revise its codes without rewriting every relationship that refers to the product. A typical products record might include the identifier, SKU, name, unit of measure, description, and an active flag.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Catalogs rarely stay neat for long. A supplier gets bought out and changes its name; someone splits a product category; an old variant disappears from the price list. There&#8217;s little reason to keep every one of those details in the products table. Categories and manufacturers can sit in separate tables when they need their own maintenance, while attributes that belong to a particular variant can be stored alongside that variant. JSON columns are another option for the odd property that doesn&#8217;t fit the usual fields. The trouble starts when an application begins querying those properties every day. Filtering, validation, indexing, reporting: ordinary columns are often easier to manage for data the system relies on regularly.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"Keep_the_trail_of_stock_movements\"><\/span>Keep the trail of stock movements<span class=\"ez-toc-section-end\"><\/span><\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">The count is 63. Yesterday&#8217;s count was 80, and the database has no answer for the missing 17. That&#8217;s what happens when a system keeps the latest balance but has little to say about how it got there.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">An inventory_movements table fills in that gap, provided its records contain enough detail to be useful later: product, quantity, movement type, timestamp, the locations involved and a reference to the underlying transaction. A transfer between warehouses, for instance, shouldn&#8217;t look like a sale. One takes stock out of one location and puts it in another; the other removes it from inventory. Returns and manual corrections have their own meaning, too. A vague label such as adjustment might get the entry saved, but it leaves little to go on when someone tries to reconcile the figures.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">There&#8217;s also the temptation to fix a bad entry in place. An employee records 18 units shipped when the actual quantity was 13. Changing 18 to 13 makes the current record correct, at least, but erases the fact that the wrong quantity was entered. Keeping the original and recording a reversal plus the corrected movement takes more rows. It also means the history hasn&#8217;t been cleaned up at the expense of the truth.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Keeping every movement is one thing; calculating the current balance from all of them every time someone opens a product page is another. With a large history, that query can get costly. A separate table containing the latest balance is quicker to consult, though now the application has two related records to keep straight. Write the movement and update the balance in the same database transaction. If one succeeds and the other doesn&#8217;t, the history and the number shown to staff can diverge.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"Available_stock_is_a_business_rule\"><\/span>Available stock is a business rule<span class=\"ez-toc-section-end\"><\/span><\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">On-hand quantity tells you what&#8217;s recorded at a location. Available quantity answers a different question: how much can the business still commit to an order?<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>&#91;<br>\\text{Available Stock}=\\text{On-hand Stock}-\\text{Reserved Stock}<br>]<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Reserved stock is still physically present, but it has already been promised to someone. Damaged goods are a different case, as are items waiting for inspection or held in quarantine. Counting all of them as available will give the ordering service a misleading figure, even if the database\u2019s arithmetic is flawless.&nbsp;<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Accurate stock levels depend on keeping reservations, physical quantities, and unusable goods distinct.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The schema needs to reflect the way the warehouse deals with these categories. Some businesses can get by with separate on-hand and reserved quantities, as well as a clear rule for excluding unusable goods. Others need stock-status records or separate quantities for goods awaiting inspection, especially in cases where those states change independently.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">There is also a choice about where to calculate availability. An application can derive it from the underlying quantities whenever it needs the figure. That avoids storing the same information twice, but frequent calculations may become costly on a busy system. Keeping an available quantity up to date makes reads cheaper, at the expense of more complicated writes. Every operation that changes stock levels, including a cancellation or correction, must maintain that figure. A missed update leaves the application with a number that looks authoritative but is wrong.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"When_two_orders_claim_the_same_stock\"><\/span>When two orders claim the same stock<span class=\"ez-toc-section-end\"><\/span><\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Five units left on the shelf. Then two orders come in, near enough at once, each asking for four. The first request checks the stock, but hasn&#8217;t booked its four yet when the second one comes along and checks the very same balance. Both get waved through. Now the system has eight units spoken for, and the warehouse is five short.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">This is a concurrency problem, not a screen-refresh problem. The database has to prevent two transactions from claiming the same units.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">More frequent screen refreshes won&#8217;t prevent that race. The check and the reservation have to be coordinated at the database level.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">A reservation can be handled in one database operation, rather than by reading the balance first and updating it a moment later:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>UPDATE inventory\nSET reserved_quantity = reserved_quantity + 4\nWHERE product_id = 101\n\u00a0\u00a0AND warehouse_id = 3\n\u00a0\u00a0AND on_hand_quantity - reserved_quantity >= 4;<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">That last condition does the important work. If another request has already reserved the remaining stock, the update won&#8217;t match the row. The application still has to check the affected-row count; otherwise, it might report a successful reservation when nothing was changed.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">There is a catch when the reservation belongs to an order. The stock update might succeed, then the order write fail. Both changes need to live in the same transaction, so a failure doesn&#8217;t leave the database halfway through the operation.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Things get messier when an order draws stock from more than one warehouse. Updating three balances isn&#8217;t the same as updating one: the first two writes might succeed before the third hits an error. You cannot leave the database in that half-finished state, so all the changes belong in one transaction. Locks on individual rows can keep two requests from altering the same balance simultaneously. The downside shows up when traffic picks up, because requests start queuing behind one another. With optimistic concurrency control, the application checks for a clash when it tries to save the change. A conflict means another attempt, or an error for the caller. If the same rows are being fought over repeatedly, those retries can become a problem of their own.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Indexes tend to outlive the queries they were added for. Someone adds one to speed up a balance lookup, then another for a report, and both stick around as the table grows. A composite index on product_id and warehouse_id is useful when stock is looked up by that combination. The timestamp index is less of a given; it earns its keep only if the queries make use of it. Each stock movement also means index entries have to be maintained, adding to the work on a table that&#8217;s already busy with writes. Before adding another index, inspect the execution plan and see where the query actually spends its time. Otherwise, you may be carrying extra index overhead for little gain.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"Getting_changes_onto_the_screen\"><\/span>Getting changes onto the screen<span class=\"ez-toc-section-end\"><\/span><\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">The database can be up to date while the page isn&#8217;t. A reservation commits successfully, but a warehouse employee still sees the previous quantity on screen. If the employee picks against that figure, the interface has become a liability even though the database itself is correct.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Committing the stock change doesn&#8217;t mean every part of the application knows about it yet. An event can carry the news to a dashboard, a cache, or the service handling orders, each of which may need to do something different with it. WebSockets and server-sent events can keep a browser supplied with fresh figures, but the connection between server and screen is another place where things go sideways. A laptop goes offline; a message arrives after a newer one; the connection drops before the update gets through. The screen can end up showing yesterday&#8217;s truth, so to speak, even if the database is already up to date. That stale figure must never decide whether an order gets reserved. The application has to check the live inventory record and apply the reservation rules when the request comes in.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">There is a failure case that deserves attention. The database commits, then the application crashes before publishing the event. The stock change is saved, but other components never hear about it.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The transactional outbox pattern deals with that gap by recording the event in an outbox table as part of the same transaction as the inventory change. A worker picks up pending events and publishes them. If the worker fails and retries, an event may be delivered more than once, so consumers should use idempotent processing or deduplication based on stable event IDs. Without that safeguard, a repeated event can trigger the same business action twice.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">There is another detail worth keeping in mind when designing the event payload. The receiving service usually needs the product and location identifiers, an event ID, and enough information to understand what changed. Sending over a whole customer record or unrelated pricing details just creates extra exposure and more data to maintain.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">WebSockets and server-sent events can provide real-time updates to browser interfaces. They don&#8217;t, however, decide whether an order can be fulfilled. A screen is a view of inventory, not the authority on its availability. Before confirming a reservation, the application must check the current authoritative state and enforce the relevant business rules.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"Data_types_and_constraints\"><\/span>Data types and constraints<span class=\"ez-toc-section-end\"><\/span><\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">The server&#8217;s timestamp isn&#8217;t always the one the business cares about. A delivery might be scanned at 09:15, then sit on an offline handheld until the device reconnects at 11:40. Both times are correct, but they describe different things. If the database keeps only the upload time, the original scan time is gone. Give the two timestamps separate columns instead of making one do double duty. UTC is usually the least troublesome choice for storing them consistently. Warehouse operations still run on local time, though: shift handovers, end-of-day counts and cutoff rules may depend on the clock on the wall, not the one in the server room.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">There are other ways for bad data to slip in. An imported SKU duplicates an existing one. A balance turns up for a warehouse that was never registered. A stock correction produces a negative quantity, although that operation shouldn&#8217;t permit one. The database can catch these cases with unique constraints, foreign keys, and CHECK constraints. It&#8217;s worth doing even when the application already validates input: imports and background jobs don&#8217;t necessarily pass through the same checks as a form used by warehouse staff.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Schema changes have a habit of turning up old assumptions. A new constraint might fail on records that have been sitting in the database for years. Change a quantity column&#8217;s numeric type and some report, integration, or bit of application code may stop handling it as expected. So the migration needs more than a quick check that the SQL runs: inspect the existing data, find the rows that won&#8217;t fit, and work out how to deal with them before deployment. There&#8217;s another wrinkle when releases overlap. If the old application is still running while the new version rolls out, both versions need to cope with the schema during that window.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The awkward part is changing these decisions after the tables have filled up. Add a constraint and the migration may stop because existing rows violate it. Change a quantity column and an integration may fail because it expects the old type. Before deploying, inspect the actual data and decide how to handle records that don&#8217;t fit the new rules. A migration also needs to account for the application versions running during the rollout; not every service will necessarily switch over at once.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"Test_what_happens_when_things_go_wrong\"><\/span>Test what happens when things go wrong<span class=\"ez-toc-section-end\"><\/span><\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Creating the tables and getting a few successful queries back proves very little. Try reserving the same last units from two sessions. Cancel an order after its stock has been allocated. Receive only part of a purchase order, transfer goods between warehouses, then process a return against a closed order. These aren&#8217;t exotic edge cases for an inventory application; they&#8217;re ordinary operations that tend to expose gaps in the model.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">After a test fails, the database is worth a look before anything else. Maybe the order row is there, but the reservation never got written. Or the balance changed and no movement explains why. Compare the records rather than relying on the test result alone. If several writes belong to one operation, a failure halfway through shouldn&#8217;t leave the successful ones sitting there on their own.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">A few hundred products won&#8217;t put much strain on a database. Millions of movement records might. Test with enough data to make the usual queries work for their living, and look at the execution plans to see where the time goes. Read speed is only part of it. An index can make a report quicker, yet every incoming stock movement has to maintain that index too. Under a busy write load, the extra work adds up. Check both before deciding the index is worth keeping.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Migrations can break a working system in quieter ways. Suppose a new uniqueness rule is introduced after duplicate product-location records have accumulated. The constraint won&#8217;t simply tidy them up; it may fail to apply at all. The records need to be reconciled first, with a clear decision about which quantity is correct. For changes to live tables, plan the rollout so that the old and new application versions can coexist for as long as deployment requires.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">A schema can look fine on paper and still leave awkward questions once real orders start coming through. Two customers want the last few units. A return comes in weeks after the sale. Someone changes a balance, but the movement records don&#8217;t explain the difference. These aren&#8217;t edge cases the database can safely leave for staff to sort out later. The tables and constraints need to make each change traceable and prevent competing operations from quietly overwriting one another.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><\/p>\n","protected":false},"excerpt":{"rendered":"<p>A warehouse can have 40 units of a product on its shelves while the application says 43 are available. Perhaps a return was recorded twice. Perhaps an order reserved stock without updating the balance correctly. Or two requests reached the database almost simultaneously, and both were approved. Reliable inventory tracking starts with separating product details [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":2865,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_sitemap_exclude":false,"_sitemap_priority":"","_sitemap_frequency":"","footnotes":""},"categories":[2],"tags":[],"class_list":["post-2864","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-development"],"aioseo_notices":[],"_links":{"self":[{"href":"https:\/\/extendsclass.com\/blog\/wp-json\/wp\/v2\/posts\/2864","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/extendsclass.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/extendsclass.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/extendsclass.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/extendsclass.com\/blog\/wp-json\/wp\/v2\/comments?post=2864"}],"version-history":[{"count":1,"href":"https:\/\/extendsclass.com\/blog\/wp-json\/wp\/v2\/posts\/2864\/revisions"}],"predecessor-version":[{"id":2866,"href":"https:\/\/extendsclass.com\/blog\/wp-json\/wp\/v2\/posts\/2864\/revisions\/2866"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/extendsclass.com\/blog\/wp-json\/wp\/v2\/media\/2865"}],"wp:attachment":[{"href":"https:\/\/extendsclass.com\/blog\/wp-json\/wp\/v2\/media?parent=2864"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/extendsclass.com\/blog\/wp-json\/wp\/v2\/categories?post=2864"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/extendsclass.com\/blog\/wp-json\/wp\/v2\/tags?post=2864"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}