{"id":2198,"date":"2013-01-22T13:14:59","date_gmt":"2013-01-22T11:14:59","guid":{"rendered":"https:\/\/test.viaboxx.de\/2013\/01\/22\/the-making-of-an-event-driven-stateless-gui\/"},"modified":"2021-08-11T09:21:52","modified_gmt":"2021-08-11T09:21:52","slug":"the-making-of-an-event-driven-stateless-gui","status":"publish","type":"post","link":"https:\/\/www.viaboxx.de\/en\/blog\/the-making-of-an-event-driven-stateless-gui\/","title":{"rendered":"The Making of an Event-driven Stateless GUI"},"content":{"rendered":"\n<p>For the last few months we\u2019ve been working hard on a new invention here at Viaboxx Systems, and now we think it\u2019s time to tell the world about our recent accomplishments.<\/p>\n\n\n\n<p>A lot of the stuff we do at work goes into creating GUIs for touch-screens, like the ones you find on ticket-machines and ATMs. One of our most favorite domains is creating frontends for parcel delivery machines, like the ones you see featured on&nbsp;<a style=\"color: #c20b1d;\" href=\"http:\/\/www.viaboxx.de\/\">viaboxx.de<\/a>.<\/p>\n\n\n\n<p>We used to do the GUI-bits of our software using Adobe Flash or Flex for many years, but we had our fair share of problems with this.<\/p>\n\n\n\n<p>Flex was good at creating an attractive and snappy interface well suited for (single) touch-screens, but was cumbersome to develop and maintain.<\/p>\n\n\n\n<p>Much of the user state and business logic was contained in the Flex layer, which again was tricky to test automatically and debug.<\/p>\n\n\n\n<p>Now, as\u00a0<a style=\"color: #c20b1d;\" href=\"http:\/\/isflashdeadyet.com\/\">Flash is slowly dying<\/a>, we decided it was about time to try something different. And the only real option for us was to try doing something with HTML5 and JavaScript, in a so-called\u00a0single-page application.<\/p>\n\n\n\n<div class=\"wp-block-image\"><figure class=\"aligncenter size-large is-resized\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/www.viaboxx.de\/wp-content\/uploads\/2021\/08\/hSAbfAyD-fX6h5gFBL7yFJ_7eIm9udpv7T9KR8EjWvGqRkfwr2udSM8mRNd0gX6mpgs2000-1024x602.png\" alt=\"\" class=\"wp-image-7019\" width=\"512\" height=\"301\" srcset=\"https:\/\/www.viaboxx.de\/wp-content\/uploads\/2021\/08\/hSAbfAyD-fX6h5gFBL7yFJ_7eIm9udpv7T9KR8EjWvGqRkfwr2udSM8mRNd0gX6mpgs2000-1024x602.png 1024w, https:\/\/www.viaboxx.de\/wp-content\/uploads\/2021\/08\/hSAbfAyD-fX6h5gFBL7yFJ_7eIm9udpv7T9KR8EjWvGqRkfwr2udSM8mRNd0gX6mpgs2000-300x176.png 300w, https:\/\/www.viaboxx.de\/wp-content\/uploads\/2021\/08\/hSAbfAyD-fX6h5gFBL7yFJ_7eIm9udpv7T9KR8EjWvGqRkfwr2udSM8mRNd0gX6mpgs2000-768x451.png 768w, https:\/\/www.viaboxx.de\/wp-content\/uploads\/2021\/08\/hSAbfAyD-fX6h5gFBL7yFJ_7eIm9udpv7T9KR8EjWvGqRkfwr2udSM8mRNd0gX6mpgs2000-1536x902.png 1536w, https:\/\/www.viaboxx.de\/wp-content\/uploads\/2021\/08\/hSAbfAyD-fX6h5gFBL7yFJ_7eIm9udpv7T9KR8EjWvGqRkfwr2udSM8mRNd0gX6mpgs2000.png 1600w\" sizes=\"(max-width: 512px) 100vw, 512px\" \/><\/figure><\/div>\n\n\n\n<p>Typical Web applications rely on gradually building a session, following a given navigational path to end up in a given state. For example, to see what the &#8220;My Orders&#8221; page looks like, you need to log in and click on the link.\u00a0<\/p>\n\n\n\n<div class=\"wp-block-image\"><figure class=\"aligncenter size-large is-resized\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/www.viaboxx.de\/wp-content\/uploads\/2021\/08\/kdE-oLCeLLCG6AHuDuMmxsFX0vr_hbTtwt_fOLPHB0kzCflAddauvw0W4fyLShU7kgs2000-1024x602.png\" alt=\"\" class=\"wp-image-7021\" width=\"512\" height=\"301\" srcset=\"https:\/\/www.viaboxx.de\/wp-content\/uploads\/2021\/08\/kdE-oLCeLLCG6AHuDuMmxsFX0vr_hbTtwt_fOLPHB0kzCflAddauvw0W4fyLShU7kgs2000-1024x602.png 1024w, https:\/\/www.viaboxx.de\/wp-content\/uploads\/2021\/08\/kdE-oLCeLLCG6AHuDuMmxsFX0vr_hbTtwt_fOLPHB0kzCflAddauvw0W4fyLShU7kgs2000-300x176.png 300w, https:\/\/www.viaboxx.de\/wp-content\/uploads\/2021\/08\/kdE-oLCeLLCG6AHuDuMmxsFX0vr_hbTtwt_fOLPHB0kzCflAddauvw0W4fyLShU7kgs2000-768x451.png 768w, https:\/\/www.viaboxx.de\/wp-content\/uploads\/2021\/08\/kdE-oLCeLLCG6AHuDuMmxsFX0vr_hbTtwt_fOLPHB0kzCflAddauvw0W4fyLShU7kgs2000-1536x902.png 1536w, https:\/\/www.viaboxx.de\/wp-content\/uploads\/2021\/08\/kdE-oLCeLLCG6AHuDuMmxsFX0vr_hbTtwt_fOLPHB0kzCflAddauvw0W4fyLShU7kgs2000.png 1600w\" sizes=\"(max-width: 512px) 100vw, 512px\" \/><\/figure><\/div>\n\n\n\n<p class=\"has-text-align-left\">You need to have the right state in both client and serve to display the &#8220;My Orders&#8221; page properly.<\/p>\n\n\n\n<div class=\"wp-block-image\"><figure class=\"aligncenter size-large is-resized\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/www.viaboxx.de\/wp-content\/uploads\/2021\/08\/1DEsslgIvpDmavItDXlMJSDKye0NGMT46AyCg2dpR3nK2Cl1wZHPvVZajmKWVLs2VAs2000-1024x602.png\" alt=\"\" class=\"wp-image-7023\" width=\"512\" height=\"301\" srcset=\"https:\/\/www.viaboxx.de\/wp-content\/uploads\/2021\/08\/1DEsslgIvpDmavItDXlMJSDKye0NGMT46AyCg2dpR3nK2Cl1wZHPvVZajmKWVLs2VAs2000-1024x602.png 1024w, https:\/\/www.viaboxx.de\/wp-content\/uploads\/2021\/08\/1DEsslgIvpDmavItDXlMJSDKye0NGMT46AyCg2dpR3nK2Cl1wZHPvVZajmKWVLs2VAs2000-300x176.png 300w, https:\/\/www.viaboxx.de\/wp-content\/uploads\/2021\/08\/1DEsslgIvpDmavItDXlMJSDKye0NGMT46AyCg2dpR3nK2Cl1wZHPvVZajmKWVLs2VAs2000-768x451.png 768w, https:\/\/www.viaboxx.de\/wp-content\/uploads\/2021\/08\/1DEsslgIvpDmavItDXlMJSDKye0NGMT46AyCg2dpR3nK2Cl1wZHPvVZajmKWVLs2VAs2000-1536x902.png 1536w, https:\/\/www.viaboxx.de\/wp-content\/uploads\/2021\/08\/1DEsslgIvpDmavItDXlMJSDKye0NGMT46AyCg2dpR3nK2Cl1wZHPvVZajmKWVLs2VAs2000.png 1600w\" sizes=\"(max-width: 512px) 100vw, 512px\" \/><\/figure><\/div>\n\n\n\n<p>This pattern has given us (and probably many others) a lot of headache over the years. Too much state and logic on the client side allows for GUI bugs that are hard to replicate.<\/p>\n\n\n\n<p>Testing and debugging on the client side also tends to be below-par of what we can do with server-side code.<br>What we have done is to remove virtually all state on the client. All logic is on the server side.<\/p>\n\n\n\n<div class=\"wp-block-image\"><figure class=\"aligncenter is-resized\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/lh4.googleusercontent.com\/7XW-X8gZ8JIBRroSlOBhxMiGgxL4GL64rp7kByrsVsMz8sG_2NTPtCPjI1SwOxqmOYFVXgaX-rgRArbzxFjDh-7g6EodcicEP81k4RDZgR9JqX3tnuiW\" alt=\"\" width=\"600\" height=\"352\"\/><\/figure><\/div>\n\n\n\n<p>Every button clicked in the client typically results in a round-trip to the server, where a state machine is told what the user did, then spits back a new screen for the client to render.<\/p>\n\n\n\n<div class=\"wp-block-image\"><figure class=\"aligncenter size-large is-resized\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/www.viaboxx.de\/wp-content\/uploads\/2013\/01\/qczwS2hWWUTJAVrqOO99_detMl3xWPYJm1Kgwi-Bs9IybfAQpIVEph3Mw9WfR0FKOws2000-1024x602.png\" alt=\"\" class=\"wp-image-7014\" width=\"512\" height=\"301\" srcset=\"https:\/\/www.viaboxx.de\/wp-content\/uploads\/2013\/01\/qczwS2hWWUTJAVrqOO99_detMl3xWPYJm1Kgwi-Bs9IybfAQpIVEph3Mw9WfR0FKOws2000-1024x602.png 1024w, https:\/\/www.viaboxx.de\/wp-content\/uploads\/2013\/01\/qczwS2hWWUTJAVrqOO99_detMl3xWPYJm1Kgwi-Bs9IybfAQpIVEph3Mw9WfR0FKOws2000-300x176.png 300w, https:\/\/www.viaboxx.de\/wp-content\/uploads\/2013\/01\/qczwS2hWWUTJAVrqOO99_detMl3xWPYJm1Kgwi-Bs9IybfAQpIVEph3Mw9WfR0FKOws2000-768x451.png 768w, https:\/\/www.viaboxx.de\/wp-content\/uploads\/2013\/01\/qczwS2hWWUTJAVrqOO99_detMl3xWPYJm1Kgwi-Bs9IybfAQpIVEph3Mw9WfR0FKOws2000-1536x902.png 1536w, https:\/\/www.viaboxx.de\/wp-content\/uploads\/2013\/01\/qczwS2hWWUTJAVrqOO99_detMl3xWPYJm1Kgwi-Bs9IybfAQpIVEph3Mw9WfR0FKOws2000.png 1600w\" sizes=\"(max-width: 512px) 100vw, 512px\" \/><\/figure><\/div>\n\n\n\n<p><br>The mechanics of the statemachine is also very interesting, but we\u2019ll come back to that in another blog post.<\/p>\n\n\n\n<p>This round-trip may sound slow for a touch screen that should be fast and responsive, but it works well for four reasons:<\/p>\n\n\n\n<p>1) Server is running on the actual same machine (PC) as a client\/browser<br>2) We use\u00a0WebSockets, so events from the server have an immediate effect on the client.<br>3) Some smart caching and JavaScript to update the page quickly.<br>4) We have very simple screens with small payloads.<\/p>\n\n\n\n<p>So what does one of these screens actually look like? Here\u2019s an example:<br><\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">[codesyntax lang=\"javascript\"]\n\"decision\": {\n\"def\": {\n\"headline\": \"Please decide\",\n\"question\": \"Is yellow more green than blue?\",\n\"type\": \"decision\"\n\"events\": [\n{\n\"id\": \"yes\",\n\"text\": \"Yes\"\n},\n{\n\"id\": \"no\",\n\"text\": \"No\"\n}\n],\n}\n}\n[\/codesyntax]<\/pre>\n\n\n\n<p>Pure JSON that represents the entire state needed to display a screen to the user.<\/p>\n\n\n\n<p class=\"has-text-align-center\">This JSON is sent to the browser, it gets run through a generator (<a style=\"color: #c20b1d;\" href=\"http:\/\/mustache.github.com\/\">Mustache<\/a>) which turns it into HTML, and then we quickly \u201cswap\u201d the new screen into the visible window. After a little styling it up with CSS, it looks like this:<br><img decoding=\"async\" src=\"https:\/\/lh5.googleusercontent.com\/LvLir2t7YVxbNjmdRUqS2nBWBIRjgiPVfMov8iwJfoJaSoTJz1UtrJaA3F-PHDCc6TFGH34JDE9dKhaiRAmSf98DpCftqRt0MEi_F8pnqbX9IdHBNAN4\" alt=\"\" width=\"583px;\" height=\"438px;\"><\/p>\n\n\n\n<p>It is really great to have this loose coupling between what the user sees, and what the server knows. Let\u2019s see why.<\/p>\n\n\n\n<p class=\"has-medium-font-size\"><strong>Advantages of this setup?<\/strong><\/p>\n\n\n\n<p class=\"has-normal-font-size\"><strong>Doing web design without having to click around in the application<\/strong><\/p>\n\n\n\n<p>You don\u2019t have to work the server side into the right state for doing some web design on a particular page. You only need a snippet of JSON (as the one mentioned earlier).<\/p>\n\n\n\n<p>Here I\u2019ll demo our little screen-IDE. It runs on a local little Node Server, and turns JSON into visible screens on the fly:<\/p>\n\n\n\n<p><meta charset=\"utf-8\">You can choose from a \u00a0prepared set of JSON examples, or you can copy\/paste in screens from the log files, and then you can adjust the contents by hand to get the condition you want to see.<\/p>\n\n\n\n<p>This provides\u00a0immediate\u00a0feedback to the web designer. After changing CSS (or LESS which we actually use), Mustache-template or JavaScript we only need to refresh the browser to see the latest content. We can also simulate some simple server-side events by passing some JavaScript to Node (look for the Script panel around 0:36 in the video above).<\/p>\n\n\n\n<p class=\"has-normal-font-size\"><strong>Test the GUI in any way possible<\/strong><\/p>\n\n\n\n<p>Traditionally, there are many disadvantages to test the application using the GUI, but many developers do it anyway because they have no other way to test the GUI, and they can\u2019t run the GUI without the rest of the application.<\/p>\n\n\n\n<p>In our setup, we can for example test that&#8230;<\/p>\n\n\n\n<p>* correct screen object is produced by the state machine (pure unit-test)<br>* expected JSON is produced from the given screen (pure unit-test)<br>* correct HTML is produced from given JSON (for this we need a browser with a JavaScript runtime, but it can be headless, like\u00a0<a style=\"color: #c20b1d;\" href=\"http:\/\/phantomjs.org\/\">PhantomJS<\/a>)<br>* HTML from a screen looks OK (taking screenshots using PhantomJS)<br>* HTML from a screen looks just right (this requires the use of a production browser on the production operating system, and taking screenshots with\u00a0<a style=\"color: #c20b1d;\" href=\"http:\/\/seleniumhq.org\/\">Selenium\/WebDriver<\/a>)<\/p>\n\n\n\n<p>We combine all of these for creating tests with different intentions: we have a lot of the ones closer to the top, and fewer of those further down the list because they are more expensive in terms of resources, and they break easily.<\/p>\n\n\n\n<p>The magic in this mix is that we are able to distinguish between (a) the right content (screens) coming from the server, and (b) the given screen looking correct in the client. This division allows us to keep our tests incredibly fast: The state machines and the screens are exercised by pure unit tests, while various browser tests create screenshots for given screens.<\/p>\n\n\n\n<p>We can also test larger parts of the stack together, or in phases. We do the latter when we generate documentation and screenshots based on our story-tests (but we mock out hardware and remote services):<\/p>\n\n\n\n<p>&#8211; Story-tests generate documentation with embedded JSON which will be replaced with real screenshots<br>&#8211; The documentation is piped through a script, where the JSON is rendered into real screenshots<\/p>\n\n\n\n<p>It\u2019s all incredibly fast, and we don\u2019t have to fire up a real JVM application server &#8211; we can just do the screen rendering in the Node simulator.<\/p>\n\n\n\n<p class=\"has-normal-font-size\"><strong>See exactly what the user saw on-screen at a given time<\/strong><\/p>\n\n\n\n<p>Let&#8217;s envision this scenario: Someone delivers a package to a machine, entering the phone number for those who should receive the pickup code on a text message. Then the recipient comes by later, enters the pickup code, and takes their package.:<\/p>\n\n\n\n<p><meta charset=\"utf-8\">Since we log most of the things going on in a machine, including the screens that are sent, it should be easy to parse the log file for a sequence of screens that occurred on the machine for a given time, and then play it back again. <\/p>\n\n\n\n<p><meta charset=\"utf-8\">So we did just that, and after a few days of development we had created Sherlog:<\/p>\n\n\n\n<p><meta charset=\"utf-8\">Not only can we fast-forward and backward through the user\u2019s experience at any pace, we can also copy the screens out of here and then work with them in the simulator if we find a GUI bug or something we want to improve on.<\/p>\n\n\n\n<p>It did not take much code to achieve this: a log parser and a little JavaScript. The neat timeline widget is created using&nbsp;<a style=\"color: #c20b1d;\" href=\"http:\/\/almende.github.com\/chap-links-library\/timeline.html\">this Timeline<\/a>&nbsp;library.<\/p>\n\n\n\n<p>We can\u2019t see where the user touches the screen yet, but we are working on that too.<\/p>\n\n\n\n<p>Looking at Sherlog, you might be thinking that this feels a bit like&nbsp;<a style=\"color: #c20b1d;\" href=\"http:\/\/martinfowler.com\/eaaDev\/EventSourcing.html\">Event Sourcing<\/a>. Usually, Event Sourcing is used for events in the domain layer, for things like package tracking and logistics, but it is certainly incredibly powerful to use events to drive the GUI as well.<\/p>\n\n\n\n<p class=\"has-medium-font-size\"><strong>Conclusion<\/strong><\/p>\n\n\n\n<p>We do not believe that this architecture is suitable for all applications, but we&#8217;ve been playing with the idea of using it on proper web applications, especially where there is interaction in the shape of a wizard or something similar to what we\u2019ve got on our machines.<\/p>\n\n\n\n<p>Moreover, it is incredibly fun to work with this technology stack. There is hardly a sprint gone by where we aren\u2019t amazed at what we have produced, and how easy it was to get it working.<\/p>\n\n\n\n<p>We hope that this will inspire someone out there to try out something similar, and stay tuned for further blog posts about our architecture.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>For the last few months we\u2019ve been working hard on a new invention here at Viaboxx Systems, and now we think it\u2019s time to tell the [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":7030,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_uag_custom_page_level_css":"","site-sidebar-layout":"default","site-content-layout":"default","ast-site-content-layout":"default","site-content-style":"default","site-sidebar-style":"default","ast-global-header-display":"","ast-banner-title-visibility":"","ast-main-header-display":"","ast-hfb-above-header-display":"","ast-hfb-below-header-display":"","ast-hfb-mobile-header-display":"","site-post-title":"","ast-breadcrumbs-content":"","ast-featured-img":"","footer-sml-layout":"","ast-disable-related-posts":"","theme-transparent-header-meta":"default","adv-header-id-meta":"","stick-header-meta":"default","header-above-stick-meta":"","header-main-stick-meta":"","header-below-stick-meta":"","astra-migrate-meta-layouts":"default","ast-page-background-enabled":"default","ast-page-background-meta":{"desktop":{"background-color":"","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"tablet":{"background-color":"","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"mobile":{"background-color":"","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""}},"ast-content-background-meta":{"desktop":{"background-color":"var(--ast-global-color-5)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"tablet":{"background-color":"var(--ast-global-color-5)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"mobile":{"background-color":"var(--ast-global-color-5)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""}},"footnotes":""},"categories":[76,78],"tags":[],"class_list":["post-2198","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-code","category-parcel-machines"],"uagb_featured_image_src":{"full":["https:\/\/www.viaboxx.de\/wp-content\/uploads\/2013\/01\/Blog-Post-the-making-of-an-event-driven-stateless-gui.jpg",2200,700,false],"thumbnail":["https:\/\/www.viaboxx.de\/wp-content\/uploads\/2013\/01\/Blog-Post-the-making-of-an-event-driven-stateless-gui-150x150.jpg",150,150,true],"medium":["https:\/\/www.viaboxx.de\/wp-content\/uploads\/2013\/01\/Blog-Post-the-making-of-an-event-driven-stateless-gui-300x95.jpg",300,95,true],"medium_large":["https:\/\/www.viaboxx.de\/wp-content\/uploads\/2013\/01\/Blog-Post-the-making-of-an-event-driven-stateless-gui-768x244.jpg",768,244,true],"large":["https:\/\/www.viaboxx.de\/wp-content\/uploads\/2013\/01\/Blog-Post-the-making-of-an-event-driven-stateless-gui-1024x326.jpg",1024,326,true],"1536x1536":["https:\/\/www.viaboxx.de\/wp-content\/uploads\/2013\/01\/Blog-Post-the-making-of-an-event-driven-stateless-gui-1536x489.jpg",1536,489,true],"2048x2048":["https:\/\/www.viaboxx.de\/wp-content\/uploads\/2013\/01\/Blog-Post-the-making-of-an-event-driven-stateless-gui-2048x652.jpg",2048,652,true]},"uagb_author_info":{"display_name":"Simon Tiffert","author_link":"https:\/\/www.viaboxx.de\/en\/blog\/author\/simon-tiffertviaboxx-de\/"},"uagb_comment_info":3,"uagb_excerpt":"For the last few months we\u2019ve been working hard on a new invention here at Viaboxx Systems, and now we think it\u2019s time to tell the [&hellip;]","_links":{"self":[{"href":"https:\/\/www.viaboxx.de\/en\/wp-json\/wp\/v2\/posts\/2198"}],"collection":[{"href":"https:\/\/www.viaboxx.de\/en\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.viaboxx.de\/en\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.viaboxx.de\/en\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.viaboxx.de\/en\/wp-json\/wp\/v2\/comments?post=2198"}],"version-history":[{"count":5,"href":"https:\/\/www.viaboxx.de\/en\/wp-json\/wp\/v2\/posts\/2198\/revisions"}],"predecessor-version":[{"id":7028,"href":"https:\/\/www.viaboxx.de\/en\/wp-json\/wp\/v2\/posts\/2198\/revisions\/7028"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.viaboxx.de\/en\/wp-json\/wp\/v2\/media\/7030"}],"wp:attachment":[{"href":"https:\/\/www.viaboxx.de\/en\/wp-json\/wp\/v2\/media?parent=2198"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.viaboxx.de\/en\/wp-json\/wp\/v2\/categories?post=2198"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.viaboxx.de\/en\/wp-json\/wp\/v2\/tags?post=2198"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}