{"id":435,"date":"2014-03-10T14:50:03","date_gmt":"2014-03-10T14:50:03","guid":{"rendered":"https:\/\/gosqeng.test\/?p=435"},"modified":"2019-11-28T12:17:16","modified_gmt":"2019-11-28T12:17:16","slug":"admin-systems-in-node","status":"publish","type":"post","link":"https:\/\/www.gosquared.com\/blog\/admin-systems-in-node","title":{"rendered":"Admin and User Account Systems in Node.js"},"content":{"rendered":"<p><img decoding=\"async\" src=\"https:\/\/static.gosquared.com\/images\/liquidicity\/14_03_10_adminnode_01.png\" alt=\"Admin Systems in Node.js\"><\/p>\n<p>At GoSquared, we recently migrated our internal admin and user account systems from PHP to Node.js. It brought up some interesting challenges, very different to those that we&#8217;re usually faced with, like <a href=\"https:\/\/www.gosquared.com\/blog\/optimising-nginx-node-js-and-networking-for-heavy-workloads\">optimising Node.js for heavy workloads<\/a>.<\/p>\n<p>Due to its asynchronous nature, user management and admin applications aren&#8217;t usually written in Node.js &#8211; but it is possible to build a stable, manageable and flexible system.<\/p>\n<h2>Classes<\/h2>\n<p>Using classes is the nicest way to provide an accessible API for your other apps. Everybody will have a <code>User<\/code> class, and all classes should inherit from a <code>Base<\/code> class which provides common methods, such as <code>setAttribute<\/code>, <code>getAttribute<\/code>, <code>insert<\/code>, <code>delete<\/code> &#8211; whatever your system requires.<\/p>\n<pre><code class=\"language-javascript\">var util = require('util');\n\/\/ assume the Base class has common methods already written\nvar Base = require('.\/Base');\n\nvar User = module.exports = function(id) {\n  var self = this;\n  self.name = 'User';\n  Base.apply(this, arguments);\n  self.table = 'users';\n};\n\nutil.inherits(User, Base);\n\nUser.prototype.create = function(details, cb) {\n  var self = this;\n\n  self.insert(details, function(err) {\n    if (err) return cb(err);\n\n    \/\/ other user creation methods, such as subscribing\n    \/\/ to a mailing list etc. etc.\n    cb(null);\n  });\n};\n<\/code><\/pre>\n<p><em>Note: with ES6 you can use the new classes if you prefer<\/em><\/p>\n<h2>Async<\/h2>\n<p>To avoid getting into callback hell, <a href=\"https:\/\/github.com\/caolan\/async\">async<\/a> is an essential node module. Although async can help avoid issues, some deep callback nesting is unavoidable and should be accepted &#8211; so long as larger functions are split into smaller methods. Using smaller methods and then connecting them together with async is easy and can lead to very clean and readable code. A great benefit of writing asynchronous code is that tasks can be done in parallel, making functions significantly faster in places &#8211; we were able to get a few over 20x faster after the migration.<\/p>\n<p>Assuming the methods <code>getAttributes<\/code> and <code>getSubscription<\/code> already exist&#8230;<\/p>\n<h3>The Wrong Way<\/h3>\n<pre><code class=\"language-javascript\">User.prototype.getDetails = function(cb) {\n  var self = this;\n  self.getAttributes(['name', 'email'], function(err, attributes) {\n    if (err) return cb(err);\n\n    self.getSubscription(function(err, subscription) {\n      if (err) return cb(err);\n\n      attributes.subscription = subscription;\n      cb(null, attributes);\n    });\n  });\n};\n<\/code><\/pre>\n<h3>The Right Way (cleaner and faster)<\/h3>\n<pre><code class=\"language-javascript\">User.prototype.getDetails = function(cb) {\n  var self = this;\n\n  async.parallel([\n    self.getAttributes.bind(self, ['name', 'email']),\n    self.getSubscription.bind(self)\n  ], function(err, res) {\n    if (err) return cb(err);\n    var attributes = res[0];\n    attributes.subscription = res[1];\n    cb(null, attributes);\n  });;\n};\n<\/code><\/pre>\n<h2>Cron Jobs<\/h2>\n<p>Most, if not all, admin systems will require tasks to be run on a regular schedule. We use <a href=\"https:\/\/github.com\/ncb000gt\/node-cron\">node-cron<\/a> to avoid having to maintain crontabs.<\/p>\n<p>As these systems are designed to be distributed across any number of servers, we use locking (via redis) to ensure the cronjobs are only run once. Larger jobs that, for example, loop over every user, are split into individual <a href=\"https:\/\/github.com\/simontabor\/qp\">QP<\/a> jobs and processed evenly by all servers.<\/p>\n<h2>Usage by other applications<\/h2>\n<p>All production applications at GoSquared are written in Node.js, which is great for maintainability. In the past, all admin functions were called via a secure HTTP API which had additional overhead and wasn&#8217;t terribly flexible &#8211; so much so that in places raw MySQL queries were being used rather than the API.<\/p>\n<p>The solution is to use the admin system as a node module, which exports the classes and and utilities\/handlers. This can then be required by any applications and the methods can be called directly &#8211; making everything quicker and significantly more versatile. Create an <code>index.js<\/code> (or equivalent) file that can export what&#8217;s needed&#8230;<\/p>\n<pre><code class=\"language-javascript\">var fs = require('fs');\nvar Admin = module.exports = {};\nvar classes = fs.readdirSync(__dirname + '\/src\/classes').sort();\n\nclasses.forEach(function(c) {\n  if (c.substr(-3) !== '.js') return;\n\n   \/\/ take off JS\n  var name = c.slice(0,-3);\n\n  \/\/ don't export the Base class\n  if (name === 'Base') return;\n\n  \/\/ define as a getter to improve performance\n  Object.defineProperty(Admin, name, {\n    get: function() {\n      return require(__dirname + '\/src\/classes\/' + name + '.js');\n    },\n    enumerable: true\n  });\n});\n<\/code><\/pre>\n<p>Then, in your applications, it will need to be added as a dependency (use git tags for versioning) and can be used like so&#8230;<\/p>\n<pre><code class=\"language-javascript\">\/\/ pretend we're serving an HTTP request to get a user's details\nvar admin = require('admin');\nvar User = admin.User;\n\n\/\/ app is already defined elsewhere (express)\napp.get('\/user\/:id\/details', function(req, res) {\n  var user = new User(req.param('id'));\n  user.getDetails(function(err, attributes) {\n    res.json(attributes);\n  });\n});\n<\/code><\/pre>\n<h2>Conclusions<\/h2>\n<p>Although building an admin system in Node.js may not be the obvious choice, it comes with many advantages and has sped up development here at GoSquared. Our applications run faster and are much more flexible, allowing us to optimise them and ultimately provide a better user experience.<\/p>\n<p>Questions? Comments? Let us know via the usual channels &#8211; Twitter (<a href=\"https:\/\/twitter.com\/gosquared\" target=\"_blank\" rel=\"noopener noreferrer\">@GoSquared<\/a>), <a href=\"https:\/\/facebook.com\/GoSquared\" target=\"_blank\" rel=\"noopener noreferrer\">Facebook<\/a> or <a href=\"https:\/\/gosquared.com\/support\/emails\/new\" target=\"_blank\" rel=\"noopener noreferrer\">email<\/a>.<\/p>\n<p>Want to work with us and make our user account systems even better? We&#8217;re hiring a full-stack engineer &#8211; <a href=\"https:\/\/gosquared.com\/support\/emails\/new\" target=\"_blank\" rel=\"noopener noreferrer\">get in touch<\/a>.<\/p>\n","protected":false},"excerpt":{"rendered":"<p><img decoding=\"async\" src=\"https:\/\/static.gosquared.com\/images\/liquidicity\/14_03_10_adminnode_01.png\" alt=\"Admin Systems in Node.js\"><\/p>\n<p>At GoSquared, we recently migrated our internal admin and user account systems from PHP to Node.js. It brought up some interesting challenges, very different to those that we&#8217;re usually faced with, like <a href=\"https:\/\/www.gosquared.com\/blog\/optimising-nginx-node-js-and-networking-for-heavy-workloads\">optimising Node.js for heavy workloads<\/a>.<\/p>\n","protected":false},"author":10,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[1452],"tags":[],"class_list":["post-435","post","type-post","status-publish","format-standard","hentry","category-engineering"],"yoast_head":"<!-- This site is optimized with the Yoast SEO Premium plugin v18.6 (Yoast SEO v19.0) - https:\/\/yoast.com\/wordpress\/plugins\/seo\/ -->\n<title>Admin and User Account Systems in Node.js - GoSquared Blog<\/title>\n<meta name=\"description\" content=\"We recently migrated our internal admin and user account systems from PHP to Node.js. Here&#039;s how we built a fast, efficient and maintainable system.\" \/>\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.gosquared.com\/blog\/admin-systems-in-node\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Admin and User Account Systems in Node.js\" \/>\n<meta property=\"og:description\" content=\"We recently migrated our internal admin and user account systems from PHP to Node.js. Here&#039;s how we built a fast, efficient and maintainable system.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/www.gosquared.com\/blog\/admin-systems-in-node\" \/>\n<meta property=\"og:site_name\" content=\"GoSquared Blog\" \/>\n<meta property=\"article:publisher\" content=\"https:\/\/www.facebook.com\/GoSquared\" \/>\n<meta property=\"article:published_time\" content=\"2014-03-10T14:50:03+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2019-11-28T12:17:16+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/static.gosquared.com\/images\/liquidicity\/14_03_10_adminnode_01.png\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:creator\" content=\"@simon_tabor\" \/>\n<meta name=\"twitter:site\" content=\"@GoSquared\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"Simon Tabor\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"4 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"Organization\",\"@id\":\"https:\/\/www.gosquared.com\/blog\/#organization\",\"name\":\"GoSquared\",\"url\":\"https:\/\/www.gosquared.com\/blog\/\",\"sameAs\":[\"https:\/\/instagram.com\/gosquaredteam\",\"https:\/\/www.linkedin.com\/company\/go-squared-ltd.\",\"https:\/\/www.facebook.com\/GoSquared\",\"https:\/\/twitter.com\/GoSquared\"],\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/www.gosquared.com\/blog\/#\/schema\/logo\/image\/\",\"url\":\"https:\/\/www.gosquared.com\/blog\/wp-content\/uploads\/2015\/07\/gosquared.png\",\"contentUrl\":\"https:\/\/www.gosquared.com\/blog\/wp-content\/uploads\/2015\/07\/gosquared.png\",\"width\":1270,\"height\":250,\"caption\":\"GoSquared\"},\"image\":{\"@id\":\"https:\/\/www.gosquared.com\/blog\/#\/schema\/logo\/image\/\"}},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/www.gosquared.com\/blog\/#website\",\"url\":\"https:\/\/www.gosquared.com\/blog\/\",\"name\":\"GoSquared Blog\",\"description\":\"Turn visitors into customers.\",\"publisher\":{\"@id\":\"https:\/\/www.gosquared.com\/blog\/#organization\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/www.gosquared.com\/blog\/?s={search_term_string}\"},\"query-input\":\"required name=search_term_string\"}],\"inLanguage\":\"en-US\"},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/www.gosquared.com\/blog\/admin-systems-in-node#primaryimage\",\"url\":\"https:\/\/static.gosquared.com\/images\/liquidicity\/14_03_10_adminnode_01.png\",\"contentUrl\":\"https:\/\/static.gosquared.com\/images\/liquidicity\/14_03_10_adminnode_01.png\"},{\"@type\":\"WebPage\",\"@id\":\"https:\/\/www.gosquared.com\/blog\/admin-systems-in-node#webpage\",\"url\":\"https:\/\/www.gosquared.com\/blog\/admin-systems-in-node\",\"name\":\"Admin and User Account Systems in Node.js - GoSquared Blog\",\"isPartOf\":{\"@id\":\"https:\/\/www.gosquared.com\/blog\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\/\/www.gosquared.com\/blog\/admin-systems-in-node#primaryimage\"},\"datePublished\":\"2014-03-10T14:50:03+00:00\",\"dateModified\":\"2019-11-28T12:17:16+00:00\",\"description\":\"We recently migrated our internal admin and user account systems from PHP to Node.js. Here's how we built a fast, efficient and maintainable system.\",\"breadcrumb\":{\"@id\":\"https:\/\/www.gosquared.com\/blog\/admin-systems-in-node#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/www.gosquared.com\/blog\/admin-systems-in-node\"]}]},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/www.gosquared.com\/blog\/admin-systems-in-node#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/www.gosquared.com\/blog\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Admin and User Account Systems in Node.js\"}]},{\"@type\":\"Article\",\"@id\":\"https:\/\/www.gosquared.com\/blog\/admin-systems-in-node#article\",\"isPartOf\":{\"@id\":\"https:\/\/www.gosquared.com\/blog\/admin-systems-in-node#webpage\"},\"author\":{\"@id\":\"https:\/\/www.gosquared.com\/blog\/#\/schema\/person\/71fad71f60ad33cf9356687b37aed3d0\"},\"headline\":\"Admin and User Account Systems in Node.js\",\"datePublished\":\"2014-03-10T14:50:03+00:00\",\"dateModified\":\"2019-11-28T12:17:16+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\/\/www.gosquared.com\/blog\/admin-systems-in-node#webpage\"},\"wordCount\":533,\"commentCount\":0,\"publisher\":{\"@id\":\"https:\/\/www.gosquared.com\/blog\/#organization\"},\"image\":{\"@id\":\"https:\/\/www.gosquared.com\/blog\/admin-systems-in-node#primaryimage\"},\"thumbnailUrl\":\"https:\/\/static.gosquared.com\/images\/liquidicity\/14_03_10_adminnode_01.png\",\"articleSection\":[\"Engineering\"],\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\/\/www.gosquared.com\/blog\/admin-systems-in-node#respond\"]}]},{\"@type\":\"Person\",\"@id\":\"https:\/\/www.gosquared.com\/blog\/#\/schema\/person\/71fad71f60ad33cf9356687b37aed3d0\",\"name\":\"Simon Tabor\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/www.gosquared.com\/blog\/#\/schema\/person\/image\/\",\"url\":\"https:\/\/secure.gravatar.com\/avatar\/dc920e48608646bda51d2e6e2595e8ad926cff52eba534c1d25fb1618f15b59f?s=96&d=mm&r=g\",\"contentUrl\":\"https:\/\/secure.gravatar.com\/avatar\/dc920e48608646bda51d2e6e2595e8ad926cff52eba534c1d25fb1618f15b59f?s=96&d=mm&r=g\",\"caption\":\"Simon Tabor\"},\"description\":\"Lead developer at GoSquared for integrations, partnerships and the API. Works on pretty much everything.\",\"sameAs\":[\"http:\/\/simontabor.com\",\"https:\/\/twitter.com\/simon_tabor\"],\"url\":\"https:\/\/www.gosquared.com\/blog\/author\/simontabor\"}]}<\/script>\n<!-- \/ Yoast SEO Premium plugin. -->","yoast_head_json":{"title":"Admin and User Account Systems in Node.js - GoSquared Blog","description":"We recently migrated our internal admin and user account systems from PHP to Node.js. Here's how we built a fast, efficient and maintainable system.","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.gosquared.com\/blog\/admin-systems-in-node","og_locale":"en_US","og_type":"article","og_title":"Admin and User Account Systems in Node.js","og_description":"We recently migrated our internal admin and user account systems from PHP to Node.js. Here's how we built a fast, efficient and maintainable system.","og_url":"https:\/\/www.gosquared.com\/blog\/admin-systems-in-node","og_site_name":"GoSquared Blog","article_publisher":"https:\/\/www.facebook.com\/GoSquared","article_published_time":"2014-03-10T14:50:03+00:00","article_modified_time":"2019-11-28T12:17:16+00:00","og_image":[{"url":"https:\/\/static.gosquared.com\/images\/liquidicity\/14_03_10_adminnode_01.png"}],"twitter_card":"summary_large_image","twitter_creator":"@simon_tabor","twitter_site":"@GoSquared","twitter_misc":{"Written by":"Simon Tabor","Est. reading time":"4 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Organization","@id":"https:\/\/www.gosquared.com\/blog\/#organization","name":"GoSquared","url":"https:\/\/www.gosquared.com\/blog\/","sameAs":["https:\/\/instagram.com\/gosquaredteam","https:\/\/www.linkedin.com\/company\/go-squared-ltd.","https:\/\/www.facebook.com\/GoSquared","https:\/\/twitter.com\/GoSquared"],"logo":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.gosquared.com\/blog\/#\/schema\/logo\/image\/","url":"https:\/\/www.gosquared.com\/blog\/wp-content\/uploads\/2015\/07\/gosquared.png","contentUrl":"https:\/\/www.gosquared.com\/blog\/wp-content\/uploads\/2015\/07\/gosquared.png","width":1270,"height":250,"caption":"GoSquared"},"image":{"@id":"https:\/\/www.gosquared.com\/blog\/#\/schema\/logo\/image\/"}},{"@type":"WebSite","@id":"https:\/\/www.gosquared.com\/blog\/#website","url":"https:\/\/www.gosquared.com\/blog\/","name":"GoSquared Blog","description":"Turn visitors into customers.","publisher":{"@id":"https:\/\/www.gosquared.com\/blog\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/www.gosquared.com\/blog\/?s={search_term_string}"},"query-input":"required name=search_term_string"}],"inLanguage":"en-US"},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.gosquared.com\/blog\/admin-systems-in-node#primaryimage","url":"https:\/\/static.gosquared.com\/images\/liquidicity\/14_03_10_adminnode_01.png","contentUrl":"https:\/\/static.gosquared.com\/images\/liquidicity\/14_03_10_adminnode_01.png"},{"@type":"WebPage","@id":"https:\/\/www.gosquared.com\/blog\/admin-systems-in-node#webpage","url":"https:\/\/www.gosquared.com\/blog\/admin-systems-in-node","name":"Admin and User Account Systems in Node.js - GoSquared Blog","isPartOf":{"@id":"https:\/\/www.gosquared.com\/blog\/#website"},"primaryImageOfPage":{"@id":"https:\/\/www.gosquared.com\/blog\/admin-systems-in-node#primaryimage"},"datePublished":"2014-03-10T14:50:03+00:00","dateModified":"2019-11-28T12:17:16+00:00","description":"We recently migrated our internal admin and user account systems from PHP to Node.js. Here's how we built a fast, efficient and maintainable system.","breadcrumb":{"@id":"https:\/\/www.gosquared.com\/blog\/admin-systems-in-node#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/www.gosquared.com\/blog\/admin-systems-in-node"]}]},{"@type":"BreadcrumbList","@id":"https:\/\/www.gosquared.com\/blog\/admin-systems-in-node#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/www.gosquared.com\/blog"},{"@type":"ListItem","position":2,"name":"Admin and User Account Systems in Node.js"}]},{"@type":"Article","@id":"https:\/\/www.gosquared.com\/blog\/admin-systems-in-node#article","isPartOf":{"@id":"https:\/\/www.gosquared.com\/blog\/admin-systems-in-node#webpage"},"author":{"@id":"https:\/\/www.gosquared.com\/blog\/#\/schema\/person\/71fad71f60ad33cf9356687b37aed3d0"},"headline":"Admin and User Account Systems in Node.js","datePublished":"2014-03-10T14:50:03+00:00","dateModified":"2019-11-28T12:17:16+00:00","mainEntityOfPage":{"@id":"https:\/\/www.gosquared.com\/blog\/admin-systems-in-node#webpage"},"wordCount":533,"commentCount":0,"publisher":{"@id":"https:\/\/www.gosquared.com\/blog\/#organization"},"image":{"@id":"https:\/\/www.gosquared.com\/blog\/admin-systems-in-node#primaryimage"},"thumbnailUrl":"https:\/\/static.gosquared.com\/images\/liquidicity\/14_03_10_adminnode_01.png","articleSection":["Engineering"],"inLanguage":"en-US","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/www.gosquared.com\/blog\/admin-systems-in-node#respond"]}]},{"@type":"Person","@id":"https:\/\/www.gosquared.com\/blog\/#\/schema\/person\/71fad71f60ad33cf9356687b37aed3d0","name":"Simon Tabor","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.gosquared.com\/blog\/#\/schema\/person\/image\/","url":"https:\/\/secure.gravatar.com\/avatar\/dc920e48608646bda51d2e6e2595e8ad926cff52eba534c1d25fb1618f15b59f?s=96&d=mm&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/dc920e48608646bda51d2e6e2595e8ad926cff52eba534c1d25fb1618f15b59f?s=96&d=mm&r=g","caption":"Simon Tabor"},"description":"Lead developer at GoSquared for integrations, partnerships and the API. Works on pretty much everything.","sameAs":["http:\/\/simontabor.com","https:\/\/twitter.com\/simon_tabor"],"url":"https:\/\/www.gosquared.com\/blog\/author\/simontabor"}]}},"wps_subtitle":"Saying goodbye to PHP","_links":{"self":[{"href":"https:\/\/www.gosquared.com\/blog\/wp-json\/wp\/v2\/posts\/435","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.gosquared.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.gosquared.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.gosquared.com\/blog\/wp-json\/wp\/v2\/users\/10"}],"replies":[{"embeddable":true,"href":"https:\/\/www.gosquared.com\/blog\/wp-json\/wp\/v2\/comments?post=435"}],"version-history":[{"count":0,"href":"https:\/\/www.gosquared.com\/blog\/wp-json\/wp\/v2\/posts\/435\/revisions"}],"wp:attachment":[{"href":"https:\/\/www.gosquared.com\/blog\/wp-json\/wp\/v2\/media?parent=435"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.gosquared.com\/blog\/wp-json\/wp\/v2\/categories?post=435"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.gosquared.com\/blog\/wp-json\/wp\/v2\/tags?post=435"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}