[{"data":1,"prerenderedAt":21482},["ShallowReactive",2],{"article_list_phendry_":3},[4,776,2478,2812,4298,11998,12480,13162,13471,16832,18899,19694,20469],{"_path":5,"_dir":6,"_draft":7,"_partial":7,"_locale":8,"title":9,"description":10,"tags":11,"publishDate":16,"excerpt":10,"image":17,"body":18,"_type":767,"_id":768,"_source":769,"_file":770,"_stem":771,"_extension":772,"author":773},"/phendry/2023-11-06/frontendframeworksin2024","2023-11-06",false,"","Frontend Frameworks in 2024: React, Svelte and Vue","Several years ago, Art+Logic settled on Vue.js as our preferred frontend Web framework. Now, in 2024, we feel it's time to revisit the frontend framework landscape to see how things have (or haven't) changed.",[12,13,14,15],"programming","react","svelte","vue","2024-05-15","/phendry/2023-11-06/img/frontend_frameworks_2024.png",{"type":19,"children":20,"toc":754},"root",[21,49,72,95,100,105,110,155,187,192,199,204,209,214,246,251,256,261,291,305,310,319,324,338,357,362,367,372,377,391,399,404,409,414,419,431,462,468,473,496,501,507,512,517,523,528,533,538,543,549,554,744,749],{"type":22,"tag":23,"props":24,"children":25},"element","p",{},[26,29,38,40,47],{"type":27,"value":28},"text","Several years ago, Art+Logic settled on ",{"type":22,"tag":30,"props":31,"children":35},"a",{"href":32,"rel":33},"https://vuejs.org/",[34],"nofollow",[36],{"type":27,"value":37},"Vue.js",{"type":27,"value":39}," as our ",{"type":22,"tag":30,"props":41,"children":44},{"href":42,"rel":43},"https://blog.artandlogic.com/ckeefer/2020-1/why-vue",[34],[45],{"type":27,"value":46},"preferred frontend Web framework",{"type":27,"value":48},". Now, in 2024, we feel it's time to revisit the frontend framework landscape to see how things have (or haven't) changed.",{"type":22,"tag":23,"props":50,"children":51},{},[52,54,61,63,70],{"type":27,"value":53},"Up for consideration, aside from Vue, are two similar frameworks: the dominant ",{"type":22,"tag":30,"props":55,"children":58},{"href":56,"rel":57},"https://react.dev/",[34],[59],{"type":27,"value":60},"React.js",{"type":27,"value":62},", and the minimalist up-and-comer ",{"type":22,"tag":30,"props":64,"children":67},{"href":65,"rel":66},"https://svelte.dev/",[34],[68],{"type":27,"value":69},"Svelte",{"type":27,"value":71},".",{"type":22,"tag":23,"props":73,"children":74},{},[75,77,84,86,93],{"type":27,"value":76},"React, the original declarative component framework, is now over 10 years old, and is the most popular frontend framework by quite a large margin. It describes itself modestly as \"a JavaScript library for building user interfaces\" and, like Vue, it uses a runtime ",{"type":22,"tag":30,"props":78,"children":81},{"href":79,"rel":80},"https://developer.mozilla.org/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Main_features#rendering_elements",[34],[82],{"type":27,"value":83},"Virtual DOM",{"type":27,"value":85}," system to manage component updates. Components are defined in ",{"type":22,"tag":30,"props":87,"children":90},{"href":88,"rel":89},"https://react.dev/learn/writing-markup-with-jsx",[34],[91],{"type":27,"value":92},"JSX",{"type":27,"value":94},", a syntax which mixes JavaScript and HTML.",{"type":22,"tag":23,"props":96,"children":97},{},[98],{"type":27,"value":99},"Svelte distinguishes itself by providing a similar declarative component development experience to React and Vue, but without a Virtual DOM runtime; it instead outputs code to directly modify the DOM. This is intended to create extremely lean and performant pages while still providing the same capabilities as a Virtual DOM based framework. It describes itself somewhat less modestly as \"cybernetically enhanced web apps\" (formerly \"the magical disappearing UI framework\"), and is the youngest of the three under consideration.",{"type":22,"tag":23,"props":101,"children":102},{},[103],{"type":27,"value":104},"Vue bills itself as \"the progressive JavaScript framework\", and was initially released a year after React. It too is based on a Virtual DOM runtime but, like Svelte (and unlike React), it typically uses a template system for rendering components rather than JSX. It aims to offer the same capabilities as frameworks like React, but in a smaller and incrementally-adoptable package.",{"type":22,"tag":23,"props":106,"children":107},{},[108],{"type":27,"value":109},"We'll be comparing these frameworks in terms of:",{"type":22,"tag":111,"props":112,"children":113},"ul",{},[114,120,125,130,135,140,145,150],{"type":22,"tag":115,"props":116,"children":117},"li",{},[118],{"type":27,"value":119},"Complexity for developers",{"type":22,"tag":115,"props":121,"children":122},{},[123],{"type":27,"value":124},"Performance",{"type":22,"tag":115,"props":126,"children":127},{},[128],{"type":27,"value":129},"Ecosystem",{"type":22,"tag":115,"props":131,"children":132},{},[133],{"type":27,"value":134},"Features",{"type":22,"tag":115,"props":136,"children":137},{},[138],{"type":27,"value":139},"Small-scale viability",{"type":22,"tag":115,"props":141,"children":142},{},[143],{"type":27,"value":144},"Retrofit and migration viability",{"type":22,"tag":115,"props":146,"children":147},{},[148],{"type":27,"value":149},"Large-scale viability",{"type":22,"tag":115,"props":151,"children":152},{},[153],{"type":27,"value":154},"TypeScript Support",{"type":22,"tag":23,"props":156,"children":157},{},[158,160,167,169,176,178,185],{"type":27,"value":159},"Before we start however, a quick nomenclature distinction needs to be made between \"framework\", meaning React/Svelte/Vue, and \"meta-framework\", meaning an expansive collection of tooling built around a given framework. Examples of meta-frameworks would be ",{"type":22,"tag":30,"props":161,"children":164},{"href":162,"rel":163},"https://nextjs.org/",[34],[165],{"type":27,"value":166},"Next.js",{"type":27,"value":168}," for React, ",{"type":22,"tag":30,"props":170,"children":173},{"href":171,"rel":172},"https://nuxt.com/",[34],[174],{"type":27,"value":175},"Nuxt",{"type":27,"value":177}," for Vue, and ",{"type":22,"tag":30,"props":179,"children":182},{"href":180,"rel":181},"https://kit.svelte.dev/",[34],[183],{"type":27,"value":184},"SvelteKit",{"type":27,"value":186}," for Svelte. Each of them provide features like code-splitting, server-side rendering, link pre-fetching, a prescribed project structure, and many other things that are typically needed in larger projects. We won't be digging into these meta-frameworks in detail, mainly because there's a lot of ground to cover there and they offer largely the same types of functionality, but we won't be ignoring them either; that's because these frameworks themselves are quite narrow in scope, so it's common in practice to need to lean on the capabilities of a meta-framework in order to build complex Web applications.",{"type":22,"tag":23,"props":188,"children":189},{},[190],{"type":27,"value":191},"With that out of the way, let's start by looking at ease of use for developers.",{"type":22,"tag":193,"props":194,"children":196},"h2",{"id":195},"complexity-for-developers",[197],{"type":27,"value":198},"Complexity for Developers",{"type":22,"tag":23,"props":200,"children":201},{},[202],{"type":27,"value":203},"Ultimately, frontend frameworks have a pretty narrow scope: they help developers to organize their components, and then put HTML and CSS on the page. Consequently, it's a shame if valuable developer brainpower gets diverted to managing the complexity of a frontend framework, at the expense of delivering more value or of minimizing technical debt.",{"type":22,"tag":23,"props":205,"children":206},{},[207],{"type":27,"value":208},"Svelte, by design, is the standout winner in terms of remaining simple; it provides templated components and basic state management, defers anything fancier than that to a meta-framework like SvelteKit, and otherwise largely stays out of the developer's way. The documentation is concise, focused, and easy for a new developer to take in. That said, I find this simplicity to be a double-edged sword: by having such a narrow focus, it means that for nontrivial projects you'll almost certainly be using it in conjunction with SvelteKit or complementary libraries, at which point it's... not all that much simpler than the competition.",{"type":22,"tag":23,"props":210,"children":211},{},[212],{"type":27,"value":213},"Vue is a step up in complexity compared to Svelte, adding multiple styles for writing components (Options API vs Composition API, templates vs JSX) and a number of bells and whistles that are only possible in a framework that uses a runtime and a Virtual DOM rendering system. The multitude of ways of doing things is arguably more of a historical problem than it is a lasting complexity, since the Composition API is the clear choice going forward. What's nice about Vue is that its features are progressive: you can learn the very basics and be able to write good, idiomatic Vue code, and then introduce larger concepts as you go along.",{"type":22,"tag":23,"props":215,"children":216},{},[217,219,226,228,235,237,244],{"type":27,"value":218},"React suffers, I think, from being the first Virtual DOM component library on the scene. Its original reactivity model, for example, caused components to completely re-render much more often than necessary, and so the framework introduced ",{"type":22,"tag":30,"props":220,"children":223},{"href":221,"rel":222},"https://legacy.reactjs.org/docs/hooks-intro.html",[34],[224],{"type":27,"value":225},"Hooks",{"type":27,"value":227}," in order to offer better control and, consequently, better performance. Hooks are difficult to get right, however (",{"type":22,"tag":30,"props":229,"children":232},{"href":230,"rel":231},"https://adevnadia.medium.com/how-to-usememo-and-usecallback-you-can-remove-most-of-them-b8ef01b2020d",[34],[233],{"type":27,"value":234},"Example 1",{"type":27,"value":236},", ",{"type":22,"tag":30,"props":238,"children":241},{"href":239,"rel":240},"https://adevnadia.medium.com/react-re-renders-guide-preventing-unnecessary-re-renders-8a3d2acbdba3",[34],[242],{"type":27,"value":243},"Example 2",{"type":27,"value":245},"), and Vue and Svelte offer the same or better reactive state management without the same level of manual management. It's a similar story across much of React's APIs: through being by far the most popular framework, it has grown to have a very wide range of capabilities, but this also greatly increases the scope of what developers need to learn in order to be productive React developers.",{"type":22,"tag":193,"props":247,"children":249},{"id":248},"performance",[250],{"type":27,"value":124},{"type":22,"tag":23,"props":252,"children":253},{},[254],{"type":27,"value":255},"Performance is critical for Web apps, and frameworks achieve good performance in a number of ways, but primarily by minimizing the size of bundled scripts and by reducing the overhead of rendering components on the page.",{"type":22,"tag":23,"props":257,"children":258},{},[259],{"type":27,"value":260},"Comparing the performance of these frameworks is nuanced, because of the way Svelte's design differs from React's and Vue's. The other two each include a runtime in the page which, among other things, updates the document using Virtual DOM diffing: the runtime compares the DOM to the Virtual DOM to identify which changes have been made, and then surgically applies those changes to the DOM. Svelte on the other hand, for each place where a component could make changes to the DOM, introduces a snippet of script which applies precisely that one change, much like you might do by hand if you weren't using a component framework. This avoids the potentially-costly step of diffing the DOM in order to identify changes.",{"type":22,"tag":23,"props":262,"children":263},{},[264,266,272,274,281,282,289],{"type":27,"value":265},"As far as bundle size goes, this means that there is a break-even point between Svelte and React/Vue. A Svelte project will initially have a near-zero bundle size because there is no framework runtime to include, but on a per-component basis, it adds ",{"type":22,"tag":267,"props":268,"children":269},"em",{},[270],{"type":27,"value":271},"more",{"type":27,"value":273}," script than a Virtual DOM based framework, so for larger projects, Svelte's bundle size advantages will diminish or even reverse (",{"type":22,"tag":30,"props":275,"children":278},{"href":276,"rel":277},"https://github.com/halfnelson/svelte-it-will-scale",[34],[279],{"type":27,"value":280},"Analysis 1",{"type":27,"value":236},{"type":22,"tag":30,"props":283,"children":286},{"href":284,"rel":285},"https://github.com/yyx990803/vue-svelte-size-analysis",[34],[287],{"type":27,"value":288},"Analysis 2",{"type":27,"value":290},").",{"type":22,"tag":23,"props":292,"children":293},{},[294,296,303],{"type":27,"value":295},"Even if a large Svelte project ships more bytes of script, however, it avoids the overhead of Virtual DOM diffing, which is an advantage, and there are several tricks that can be utilized in order to compensate for having to download more script (like code-splitting or ",{"type":22,"tag":30,"props":297,"children":300},{"href":298,"rel":299},"https://kit.svelte.dev/docs/link-options#data-sveltekit-preload-data",[34],[301],{"type":27,"value":302},"link preloading",{"type":27,"value":304},"). So overall, it's very hard to say with certainty whether a given project would see a meaningful performance difference between these three frameworks.",{"type":22,"tag":23,"props":306,"children":307},{},[308],{"type":27,"value":309},"There are a couple of ways to get a rough idea, however, the first being to look at benchmarks:",{"type":22,"tag":23,"props":311,"children":312},{},[313],{"type":22,"tag":314,"props":315,"children":318},"img",{"alt":316,"src":317},"React, Svelte and Vue benchmarks. Source: https://krausest.github.io/js-framework-benchmark/current.html","/phendry/2023-11-06/img/Benchmarks.png",[],{"type":22,"tag":23,"props":320,"children":321},{},[322],{"type":27,"value":323},"Despite Svelte's claimed performance benefits, it doesn't perform meaningfully better than Vue, whereas React lags a little behind.",{"type":22,"tag":23,"props":325,"children":326},{},[327,329,336],{"type":27,"value":328},"A more realistic comparison is the aptly-named ",{"type":22,"tag":30,"props":330,"children":333},{"href":331,"rel":332},"https://medium.com/dailyjs/a-realworld-comparison-of-front-end-frameworks-2020-4e50655fe4c1",[34],[334],{"type":27,"value":335},"Real World demo application",{"type":27,"value":337},", in which Svelte performs great, Vue performs almost as well, and React again lags a little behind.",{"type":22,"tag":23,"props":339,"children":340},{},[341,343,348,350,355],{"type":27,"value":342},"The big thing to remember with these comparisons however is that these are ",{"type":22,"tag":267,"props":344,"children":345},{},[346],{"type":27,"value":347},"meticulously-crafted",{"type":27,"value":349}," submissions meant to get the best possible score; real-world performance will depend much more on how ",{"type":22,"tag":267,"props":351,"children":352},{},[353],{"type":27,"value":354},"easy",{"type":27,"value":356}," the framework makes it for time-starved developers to write performant code (see the \"Complexity for Developers\" section above). With its manually-managed hooks, React makes this considerably harder than the others, and consequently it has a reputation for making it easy for developers to inadvertently create performance problems.",{"type":22,"tag":23,"props":358,"children":359},{},[360],{"type":27,"value":361},"Overall, for mid- to large-sized Web apps, I don't think there is a strong performance reason to favour one framework over the others. Svelte may be little faster and React a little slower, but all three frameworks can be extremely fast and responsive.",{"type":22,"tag":193,"props":363,"children":365},{"id":364},"ecosystem",[366],{"type":27,"value":129},{"type":22,"tag":23,"props":368,"children":369},{},[370],{"type":27,"value":371},"No nontrivial modern Web application is created without leveraging many additional libraries to manage tasks like client-side routing, state management, authentication, or UI components, and many of these libraries integrate closely with the framework. Consequently it's a liability to use a framework which has a small ecosystem of third-party libraries to lean on, and a boon to use a framework with a rich ecosystem any many well-supported options.",{"type":22,"tag":23,"props":373,"children":374},{},[375],{"type":27,"value":376},"There's also the issue of talent, in that it is much easier to find developers with experience with a popular tool than a less popular one. The importance of this depends on how easy it is for an experienced developer to learn (see \"Complexity for Developers\" above).",{"type":22,"tag":23,"props":378,"children":379},{},[380,382,389],{"type":27,"value":381},"React has by far the biggest ecosystem of any frontend framework. In a recent ",{"type":22,"tag":30,"props":383,"children":386},{"href":384,"rel":385},"https://survey.stackoverflow.co/2023/#most-popular-technologies-webframe-prof",[34],[387],{"type":27,"value":388},"Stack Overflow Developer Survey",{"type":27,"value":390},", it dwarfs Vue and Svelte in usage among professional developers:",{"type":22,"tag":23,"props":392,"children":393},{},[394],{"type":22,"tag":314,"props":395,"children":398},{"alt":396,"src":397},"Reported usage of Web frameworks among professional developers. Source: Stack Overflow","/phendry/2023-11-06/img/Popularity.png",[],{"type":22,"tag":23,"props":400,"children":401},{},[402],{"type":27,"value":403},"For a tech lead looking to make a practical choice of framework, this really makes React the default choice if all else is equal, in my mind. If any of these frameworks could meet your project's needs, then why wouldn't you select the one that makes it easiest to fill out a team of experienced developers, and the easiest to find whatever third-party libraries your team needs in order to be productive? The further you stray from the well-travelled path, the more challenging it becomes, and so the advantages of a less-popular choice need to be quite substantial.",{"type":22,"tag":23,"props":405,"children":406},{},[407],{"type":27,"value":408},"To their credit, though, none of these frameworks have an ecosystem that feels lacking, even Svelte. Each has one or more popular meta-frameworks (Next.js for React, Nuxt for Vue, SvelteKit for Svelte) which provide the advanced and server-integrated features that modern Web apps increasingly demand, and a wide range of well-supported libraries.",{"type":22,"tag":193,"props":410,"children":412},{"id":411},"features",[413],{"type":27,"value":134},{"type":22,"tag":23,"props":415,"children":416},{},[417],{"type":27,"value":418},"While each framework's corresponding meta-frameworks compete on all sorts of advanced features, honestly... for typical projects, there are few meaningful differences between the capabilities of these frameworks. Without going into great detail about every feature they offer and how they compare, the bottom line is that React, Svelte and Vue are all capable of building feature-rich and complex Web applications, and they all offer great TypeScript support, editor plugins, and tooling.",{"type":22,"tag":23,"props":420,"children":421},{},[422,424,429],{"type":27,"value":423},"There are two notable exceptions worth mentioning. The first, and it's a small one, is that Svelte lacks \"render function\" capabilities, i.e. the ability to skip the template system and write a custom function whose output is the HTML content of a component. This limitation is fundamental to Svelte's runtime-less design. Having personally a great deal of experience working within template systems, I would say that in practice this is typically only a limitation for plug-in libraries seeking to be especially generic and flexible; I can't recall ever ",{"type":22,"tag":267,"props":425,"children":426},{},[427],{"type":27,"value":428},"needing",{"type":27,"value":430}," a render function in order to meet a project goal. The limitation is present however, and likely affects the capabilities of third-party packages.",{"type":22,"tag":23,"props":432,"children":433},{},[434,436,443,445,451,453,460],{"type":27,"value":435},"The second and more important difference is that React has native Android/iOS capabilities via ",{"type":22,"tag":30,"props":437,"children":440},{"href":438,"rel":439},"https://reactnative.dev/",[34],[441],{"type":27,"value":442},"React Native",{"type":27,"value":444}," that are unmatched by NativeScript-based tools for ",{"type":22,"tag":30,"props":446,"children":449},{"href":447,"rel":448},"https://svelte-native.technology",[34],[450],{"type":27,"value":69},{"type":27,"value":452}," and for ",{"type":22,"tag":30,"props":454,"children":457},{"href":455,"rel":456},"https://nativescript-vue.org/",[34],[458],{"type":27,"value":459},"Vue",{"type":27,"value":461},", in terms of production-readiness. If it's valuable for your project to be able to build a native mobile app using a Web developer skillset rather than native Android or iOS development skills, then React is the framework which can deliver that.",{"type":22,"tag":193,"props":463,"children":465},{"id":464},"small-scale-viability",[466],{"type":27,"value":467},"Small-scale Viability",{"type":22,"tag":23,"props":469,"children":470},{},[471],{"type":27,"value":472},"At Art+Logic we work on projects ranging from very small to reasonably large, so there is a lot of value in working with a frontend framework that is suitable for the full gamut of project sizes. This should arguably be a consideration for most teams, because most small projects could find themselves needing to scale up, and most large projects end up having small complementary projects like internal tools.",{"type":22,"tag":23,"props":474,"children":475},{},[476,478,494],{"type":27,"value":477},"One of Vue's capabilities that makes it uniquely suited for small projects is that ",{"type":22,"tag":30,"props":479,"children":482},{"href":480,"rel":481},"https://vuejs.org/guide/quick-start.html#using-vue-from-cdn",[34],[483,485,492],{"type":27,"value":484},"it can be used via a CDN ",{"type":22,"tag":486,"props":487,"children":489},"code",{"className":488},[],[490],{"type":27,"value":491},"\u003Cscript>",{"type":27,"value":493}," tag without any build system whatsoever",{"type":27,"value":495},". This means you can inject Vue onto a static page to introduce dynamic functionality to just a portion of it, or easily inject Vue into a project built using a different framework altogether. React and Svelte on the other hand are decidedly geared towards building a project within the bounds of their tooling ecosystems, limiting their small-scale flexibility.",{"type":22,"tag":23,"props":497,"children":498},{},[499],{"type":27,"value":500},"For a green-field project however, Svelte's wafer-thin bundle sizes make it a great choice for small-scale or low-complexity projects. React, having the heftiest runtime of the bunch, is perhaps less advisable for a small project, but still is perfectly serviceable.",{"type":22,"tag":193,"props":502,"children":504},{"id":503},"retrofit-and-migration-viability",[505],{"type":27,"value":506},"Retrofit and Migration Viability",{"type":22,"tag":23,"props":508,"children":509},{},[510],{"type":27,"value":511},"With the frustratingly-fast pace of evolution in frontend Web development tooling over the last couple decades, many teams are left with older projects whose choice of framework has become a hindrance to continued development. A full re-write is a risky proposition, so it's powerful to have the ability to implement a gradual extension or migration in a modern framework.",{"type":22,"tag":23,"props":513,"children":514},{},[515],{"type":27,"value":516},"Vue again is uniquely positioned in this regard: being the self-described \"progressive framework\", it is much more practical than the others to integrate into an existing project built with another framework. In React or Svelte, I would likely not attempt to anything more granular than a page-by-page conversion, but with Vue, it's easy to embed Vue within an existing codebase and build system. I once had occasion to implement a Backbone.js component which renders a Vue component, which in turns renders a Backbone.js component; you'll have to trust me that there was a good reason for doing so!",{"type":22,"tag":193,"props":518,"children":520},{"id":519},"large-scale-viability",[521],{"type":27,"value":522},"Large-scale Viability",{"type":22,"tag":23,"props":524,"children":525},{},[526],{"type":27,"value":527},"All three of these frameworks have powerful build systems and meta-frameworks which enable all sorts of advanced performance-related features (such as prefetching links, server-side rendering, code-splitting, etc). As such, they're all able to scale up and competently handle large, complex projects.",{"type":22,"tag":23,"props":529,"children":530},{},[531],{"type":27,"value":532},"I would personally have some hesitation with using Svelte on a large project however, this being for ecosystem reasons rather than technical ones. A large project is more likely to be difficult to migrate toward alternative technologies in the future, and more likely to require a wide variety of third-party libraries, both of which arguably make conservative technology choices more appropriate. In this case, that would mean choosing a framework with a larger talent pool and third-party ecosystem (i.e. React, or to a lesser extent Vue).",{"type":22,"tag":193,"props":534,"children":536},{"id":535},"typescript-support",[537],{"type":27,"value":154},{"type":22,"tag":23,"props":539,"children":540},{},[541],{"type":27,"value":542},"There's not much to be said here since all three frameworks have great TypeScript support, but it's worth highlighting that fact, since TypeScript is an excellent language choice for developing a robust codebase. It would be difficult to recommend a framework in 2024 which does not provide TypeScript as a first-class option.",{"type":22,"tag":193,"props":544,"children":546},{"id":545},"summary-and-conclusion",[547],{"type":27,"value":548},"Summary and Conclusion",{"type":22,"tag":23,"props":550,"children":551},{},[552],{"type":27,"value":553},"To grossly oversimply the above considerations into a comparison chart:",{"type":22,"tag":555,"props":556,"children":557},"table",{},[558,584],{"type":22,"tag":559,"props":560,"children":561},"thead",{},[562],{"type":22,"tag":563,"props":564,"children":565},"tr",{},[566,570,576,580],{"type":22,"tag":567,"props":568,"children":569},"th",{},[],{"type":22,"tag":567,"props":571,"children":573},{"align":572},"center",[574],{"type":27,"value":575},"React",{"type":22,"tag":567,"props":577,"children":578},{"align":572},[579],{"type":27,"value":69},{"type":22,"tag":567,"props":581,"children":582},{"align":572},[583],{"type":27,"value":459},{"type":22,"tag":585,"props":586,"children":587},"tbody",{},[588,611,630,649,668,687,706,725],{"type":22,"tag":563,"props":589,"children":590},{},[591,596,601,606],{"type":22,"tag":592,"props":593,"children":594},"td",{},[595],{"type":27,"value":119},{"type":22,"tag":592,"props":597,"children":598},{"align":572},[599],{"type":27,"value":600},"🔴",{"type":22,"tag":592,"props":602,"children":603},{"align":572},[604],{"type":27,"value":605},"🟢",{"type":22,"tag":592,"props":607,"children":608},{"align":572},[609],{"type":27,"value":610},"🟡",{"type":22,"tag":563,"props":612,"children":613},{},[614,618,622,626],{"type":22,"tag":592,"props":615,"children":616},{},[617],{"type":27,"value":124},{"type":22,"tag":592,"props":619,"children":620},{"align":572},[621],{"type":27,"value":610},{"type":22,"tag":592,"props":623,"children":624},{"align":572},[625],{"type":27,"value":605},{"type":22,"tag":592,"props":627,"children":628},{"align":572},[629],{"type":27,"value":605},{"type":22,"tag":563,"props":631,"children":632},{},[633,637,641,645],{"type":22,"tag":592,"props":634,"children":635},{},[636],{"type":27,"value":129},{"type":22,"tag":592,"props":638,"children":639},{"align":572},[640],{"type":27,"value":605},{"type":22,"tag":592,"props":642,"children":643},{"align":572},[644],{"type":27,"value":600},{"type":22,"tag":592,"props":646,"children":647},{"align":572},[648],{"type":27,"value":610},{"type":22,"tag":563,"props":650,"children":651},{},[652,656,660,664],{"type":22,"tag":592,"props":653,"children":654},{},[655],{"type":27,"value":134},{"type":22,"tag":592,"props":657,"children":658},{"align":572},[659],{"type":27,"value":605},{"type":22,"tag":592,"props":661,"children":662},{"align":572},[663],{"type":27,"value":605},{"type":22,"tag":592,"props":665,"children":666},{"align":572},[667],{"type":27,"value":605},{"type":22,"tag":563,"props":669,"children":670},{},[671,675,679,683],{"type":22,"tag":592,"props":672,"children":673},{},[674],{"type":27,"value":139},{"type":22,"tag":592,"props":676,"children":677},{"align":572},[678],{"type":27,"value":610},{"type":22,"tag":592,"props":680,"children":681},{"align":572},[682],{"type":27,"value":605},{"type":22,"tag":592,"props":684,"children":685},{"align":572},[686],{"type":27,"value":605},{"type":22,"tag":563,"props":688,"children":689},{},[690,694,698,702],{"type":22,"tag":592,"props":691,"children":692},{},[693],{"type":27,"value":144},{"type":22,"tag":592,"props":695,"children":696},{"align":572},[697],{"type":27,"value":610},{"type":22,"tag":592,"props":699,"children":700},{"align":572},[701],{"type":27,"value":610},{"type":22,"tag":592,"props":703,"children":704},{"align":572},[705],{"type":27,"value":605},{"type":22,"tag":563,"props":707,"children":708},{},[709,713,717,721],{"type":22,"tag":592,"props":710,"children":711},{},[712],{"type":27,"value":149},{"type":22,"tag":592,"props":714,"children":715},{"align":572},[716],{"type":27,"value":605},{"type":22,"tag":592,"props":718,"children":719},{"align":572},[720],{"type":27,"value":610},{"type":22,"tag":592,"props":722,"children":723},{"align":572},[724],{"type":27,"value":605},{"type":22,"tag":563,"props":726,"children":727},{},[728,732,736,740],{"type":22,"tag":592,"props":729,"children":730},{},[731],{"type":27,"value":154},{"type":22,"tag":592,"props":733,"children":734},{"align":572},[735],{"type":27,"value":605},{"type":22,"tag":592,"props":737,"children":738},{"align":572},[739],{"type":27,"value":605},{"type":22,"tag":592,"props":741,"children":742},{"align":572},[743],{"type":27,"value":605},{"type":22,"tag":23,"props":745,"children":746},{},[747],{"type":27,"value":748},"In general, React, Svelte and Vue are all excellent and production-ready frameworks, and a strong case can be made for any one of them. React offers a drastically larger ecosystem and talent pool in addition to native mobile capabilities, though it suffers from a higher complexity for developers and somewhat less reliable performance. Svelte offers cutting-edge performance, but its ecosystem is less mature. Vue sits somewhere in the middle, and offers the greatest versatility for a wide variety of project scenarios.",{"type":22,"tag":23,"props":750,"children":751},{},[752],{"type":27,"value":753},"At Art+Logic, where \"coding the 'impossible'\" can take any number of forms, versatility continues to be one of the most valuable things we look for in a framework, and Vue continues to provide the most versatility while still ticking all the other boxes we expect it to. As such, it continues to be our preferred choice for frontend Web development.",{"title":8,"searchDepth":755,"depth":755,"links":756},3,[757,759,760,761,762,763,764,765,766],{"id":195,"depth":758,"text":198},2,{"id":248,"depth":758,"text":124},{"id":364,"depth":758,"text":129},{"id":411,"depth":758,"text":134},{"id":464,"depth":758,"text":467},{"id":503,"depth":758,"text":506},{"id":519,"depth":758,"text":522},{"id":535,"depth":758,"text":154},{"id":545,"depth":758,"text":548},"markdown","content:phendry:2023-11-06:FrontendFrameworksIn2024.md","content","phendry/2023-11-06/FrontendFrameworksIn2024.md","phendry/2023-11-06/FrontendFrameworksIn2024","md",{"user":774,"name":775},"phendry","Paul Hendry",{"_path":777,"_dir":778,"_draft":7,"_partial":7,"_locale":8,"title":779,"description":780,"image":781,"tags":782,"excerpt":780,"publishDate":783,"body":784,"_type":767,"_id":2474,"_source":769,"_file":2475,"_stem":2476,"_extension":772,"author":2477},"/phendry/2023-04-02/semantichtml","2023-04-02","Don't Give Up on Semantic HTML","Since the early days of the Web, there has been tension between the ideal of \"semantic HTML\" and the practical reality of designing complex page layouts, which often could not be achieved without inserting style concerns into the document. More recently, frameworks like Tailwind CSS have emerged which challenge the very idea that semantic HTML is an ideal to strive for, and which commit to thoroughly embedding style concerns into HTML documents. With modern CSS features however, semantic HTML is more achievable than ever, and I do think it remains a worthy goal.","/phendry/2023-04-02/img/Don't Give Up on Semantic HTML.png",[12],"2024-03-01",{"type":19,"children":785,"toc":2458},[786,800,827,835,841,846,860,872,885,1176,1181,1360,1380,1386,1391,1396,1408,1413,1419,1424,1429,1457,1469,1475,1480,1500,1545,1566,1578,1600,1605,1746,1789,1803,1811,1816,1821,1957,2002,2010,2015,2027,2032,2312,2361,2366,2384,2389,2397,2425,2431,2443,2448,2453],{"type":22,"tag":23,"props":787,"children":788},{},[789,791,798],{"type":27,"value":790},"Since the early days of the Web, there has been tension between the ideal of \"semantic HTML\" and the practical reality of designing complex page layouts, which often could not be achieved without inserting style concerns into the document. More recently, frameworks like ",{"type":22,"tag":30,"props":792,"children":795},{"href":793,"rel":794},"https://tailwindcss.com/",[34],[796],{"type":27,"value":797},"Tailwind CSS",{"type":27,"value":799}," have emerged which challenge the very idea that semantic HTML is an ideal to strive for, and which commit to thoroughly embedding style concerns into HTML documents. With modern CSS features however, semantic HTML is more achievable than ever, and I do think it remains a worthy goal.",{"type":22,"tag":23,"props":801,"children":802},{},[803,805,812,814,825],{"type":27,"value":804},"To demonstrate some modern techniques for writing semantic HTML, I've built a ",{"type":22,"tag":30,"props":806,"children":809},{"href":807,"rel":808},"https://github.com/artandlogic/semantic-html-demo/",[34],[810],{"type":27,"value":811},"demo project",{"type":27,"value":813}," which can be viewed at ",{"type":22,"tag":30,"props":815,"children":818},{"href":816,"rel":817},"https://artandlogic.github.io/semantic-html-demo/",[34],[819],{"type":22,"tag":486,"props":820,"children":822},{"className":821},[],[823],{"type":27,"value":824},"artandlogic.github.io/semantic-html-demo/",{"type":27,"value":826},". It consists of a single, unchanging HTML document, with a dropdown to select between three dramatically-different page styles. I'll be referencing it later on.",{"type":22,"tag":23,"props":828,"children":829},{},[830],{"type":22,"tag":314,"props":831,"children":834},{"alt":832,"src":833},"A preview of the demo page styles","/phendry/2023-04-02/img/demo_preview.png",[],{"type":22,"tag":193,"props":836,"children":838},{"id":837},"what-is-semantic-html",[839],{"type":27,"value":840},"What is Semantic HTML?",{"type":22,"tag":23,"props":842,"children":843},{},[844],{"type":27,"value":845},"Simply put, we'll define semantic HTML as HTML which",{"type":22,"tag":847,"props":848,"children":849},"ol",{},[850,855],{"type":22,"tag":115,"props":851,"children":852},{},[853],{"type":27,"value":854},"uses relevant HTML tags to describe the meaning of page elements, and",{"type":22,"tag":115,"props":856,"children":857},{},[858],{"type":27,"value":859},"represents only the structure and content of the page, separate from any particular presentation of that content.",{"type":22,"tag":23,"props":861,"children":862},{},[863,865,870],{"type":27,"value":864},"I say \"we\" because some might consider that this stretches the definition to include a separate concept, namely the separation of concerns between HTML (for content) and CSS (for presentation). But in my mind, effectively representing the meaning of the content also means ",{"type":22,"tag":267,"props":866,"children":867},{},[868],{"type":27,"value":869},"not",{"type":27,"value":871}," obfuscating it with presentational concerns, so for the purposes of this article we'll be calling it all \"semantic HTML\".",{"type":22,"tag":23,"props":873,"children":874},{},[875,877,883],{"type":27,"value":876},"For example, a page's content could be defined in HTML with ",{"type":22,"tag":486,"props":878,"children":880},{"className":879},[],[881],{"type":27,"value":882},"\u003Cdiv>",{"type":27,"value":884}," elements:",{"type":22,"tag":886,"props":887,"children":891},"pre",{"className":888,"code":889,"language":890,"meta":8,"style":8},"language-html shiki shiki-themes github-light github-dark","\u003Cbody>\n    \u003Cdiv class=\"navbar\">\n        \u003Cdiv class=\"float-to-left\">\n            \u003Cimg class=\"logo\">\n        \u003C/div>\n        \u003Cdiv class=\"nav\">Navigation links\u003C/div>\n    \u003C/div>\n    \u003Cdiv class=\"main\">Main page content\u003C/div>\n    \u003Cdiv class=\"footer\">Page footer\u003C/div>\n\u003C/body>\n","html",[892],{"type":22,"tag":486,"props":893,"children":894},{"__ignoreMap":8},[895,918,952,981,1011,1028,1066,1083,1121,1159],{"type":22,"tag":896,"props":897,"children":900},"span",{"class":898,"line":899},"line",1,[901,907,913],{"type":22,"tag":896,"props":902,"children":904},{"style":903},"--shiki-default:#24292E;--shiki-dark:#E1E4E8",[905],{"type":27,"value":906},"\u003C",{"type":22,"tag":896,"props":908,"children":910},{"style":909},"--shiki-default:#22863A;--shiki-dark:#85E89D",[911],{"type":27,"value":912},"body",{"type":22,"tag":896,"props":914,"children":915},{"style":903},[916],{"type":27,"value":917},">\n",{"type":22,"tag":896,"props":919,"children":920},{"class":898,"line":758},[921,926,931,937,942,948],{"type":22,"tag":896,"props":922,"children":923},{"style":903},[924],{"type":27,"value":925},"    \u003C",{"type":22,"tag":896,"props":927,"children":928},{"style":909},[929],{"type":27,"value":930},"div",{"type":22,"tag":896,"props":932,"children":934},{"style":933},"--shiki-default:#6F42C1;--shiki-dark:#B392F0",[935],{"type":27,"value":936}," class",{"type":22,"tag":896,"props":938,"children":939},{"style":903},[940],{"type":27,"value":941},"=",{"type":22,"tag":896,"props":943,"children":945},{"style":944},"--shiki-default:#032F62;--shiki-dark:#9ECBFF",[946],{"type":27,"value":947},"\"navbar\"",{"type":22,"tag":896,"props":949,"children":950},{"style":903},[951],{"type":27,"value":917},{"type":22,"tag":896,"props":953,"children":954},{"class":898,"line":755},[955,960,964,968,972,977],{"type":22,"tag":896,"props":956,"children":957},{"style":903},[958],{"type":27,"value":959},"        \u003C",{"type":22,"tag":896,"props":961,"children":962},{"style":909},[963],{"type":27,"value":930},{"type":22,"tag":896,"props":965,"children":966},{"style":933},[967],{"type":27,"value":936},{"type":22,"tag":896,"props":969,"children":970},{"style":903},[971],{"type":27,"value":941},{"type":22,"tag":896,"props":973,"children":974},{"style":944},[975],{"type":27,"value":976},"\"float-to-left\"",{"type":22,"tag":896,"props":978,"children":979},{"style":903},[980],{"type":27,"value":917},{"type":22,"tag":896,"props":982,"children":984},{"class":898,"line":983},4,[985,990,994,998,1002,1007],{"type":22,"tag":896,"props":986,"children":987},{"style":903},[988],{"type":27,"value":989},"            \u003C",{"type":22,"tag":896,"props":991,"children":992},{"style":909},[993],{"type":27,"value":314},{"type":22,"tag":896,"props":995,"children":996},{"style":933},[997],{"type":27,"value":936},{"type":22,"tag":896,"props":999,"children":1000},{"style":903},[1001],{"type":27,"value":941},{"type":22,"tag":896,"props":1003,"children":1004},{"style":944},[1005],{"type":27,"value":1006},"\"logo\"",{"type":22,"tag":896,"props":1008,"children":1009},{"style":903},[1010],{"type":27,"value":917},{"type":22,"tag":896,"props":1012,"children":1014},{"class":898,"line":1013},5,[1015,1020,1024],{"type":22,"tag":896,"props":1016,"children":1017},{"style":903},[1018],{"type":27,"value":1019},"        \u003C/",{"type":22,"tag":896,"props":1021,"children":1022},{"style":909},[1023],{"type":27,"value":930},{"type":22,"tag":896,"props":1025,"children":1026},{"style":903},[1027],{"type":27,"value":917},{"type":22,"tag":896,"props":1029,"children":1031},{"class":898,"line":1030},6,[1032,1036,1040,1044,1048,1053,1058,1062],{"type":22,"tag":896,"props":1033,"children":1034},{"style":903},[1035],{"type":27,"value":959},{"type":22,"tag":896,"props":1037,"children":1038},{"style":909},[1039],{"type":27,"value":930},{"type":22,"tag":896,"props":1041,"children":1042},{"style":933},[1043],{"type":27,"value":936},{"type":22,"tag":896,"props":1045,"children":1046},{"style":903},[1047],{"type":27,"value":941},{"type":22,"tag":896,"props":1049,"children":1050},{"style":944},[1051],{"type":27,"value":1052},"\"nav\"",{"type":22,"tag":896,"props":1054,"children":1055},{"style":903},[1056],{"type":27,"value":1057},">Navigation links\u003C/",{"type":22,"tag":896,"props":1059,"children":1060},{"style":909},[1061],{"type":27,"value":930},{"type":22,"tag":896,"props":1063,"children":1064},{"style":903},[1065],{"type":27,"value":917},{"type":22,"tag":896,"props":1067,"children":1069},{"class":898,"line":1068},7,[1070,1075,1079],{"type":22,"tag":896,"props":1071,"children":1072},{"style":903},[1073],{"type":27,"value":1074},"    \u003C/",{"type":22,"tag":896,"props":1076,"children":1077},{"style":909},[1078],{"type":27,"value":930},{"type":22,"tag":896,"props":1080,"children":1081},{"style":903},[1082],{"type":27,"value":917},{"type":22,"tag":896,"props":1084,"children":1086},{"class":898,"line":1085},8,[1087,1091,1095,1099,1103,1108,1113,1117],{"type":22,"tag":896,"props":1088,"children":1089},{"style":903},[1090],{"type":27,"value":925},{"type":22,"tag":896,"props":1092,"children":1093},{"style":909},[1094],{"type":27,"value":930},{"type":22,"tag":896,"props":1096,"children":1097},{"style":933},[1098],{"type":27,"value":936},{"type":22,"tag":896,"props":1100,"children":1101},{"style":903},[1102],{"type":27,"value":941},{"type":22,"tag":896,"props":1104,"children":1105},{"style":944},[1106],{"type":27,"value":1107},"\"main\"",{"type":22,"tag":896,"props":1109,"children":1110},{"style":903},[1111],{"type":27,"value":1112},">Main page content\u003C/",{"type":22,"tag":896,"props":1114,"children":1115},{"style":909},[1116],{"type":27,"value":930},{"type":22,"tag":896,"props":1118,"children":1119},{"style":903},[1120],{"type":27,"value":917},{"type":22,"tag":896,"props":1122,"children":1124},{"class":898,"line":1123},9,[1125,1129,1133,1137,1141,1146,1151,1155],{"type":22,"tag":896,"props":1126,"children":1127},{"style":903},[1128],{"type":27,"value":925},{"type":22,"tag":896,"props":1130,"children":1131},{"style":909},[1132],{"type":27,"value":930},{"type":22,"tag":896,"props":1134,"children":1135},{"style":933},[1136],{"type":27,"value":936},{"type":22,"tag":896,"props":1138,"children":1139},{"style":903},[1140],{"type":27,"value":941},{"type":22,"tag":896,"props":1142,"children":1143},{"style":944},[1144],{"type":27,"value":1145},"\"footer\"",{"type":22,"tag":896,"props":1147,"children":1148},{"style":903},[1149],{"type":27,"value":1150},">Page footer\u003C/",{"type":22,"tag":896,"props":1152,"children":1153},{"style":909},[1154],{"type":27,"value":930},{"type":22,"tag":896,"props":1156,"children":1157},{"style":903},[1158],{"type":27,"value":917},{"type":22,"tag":896,"props":1160,"children":1162},{"class":898,"line":1161},10,[1163,1168,1172],{"type":22,"tag":896,"props":1164,"children":1165},{"style":903},[1166],{"type":27,"value":1167},"\u003C/",{"type":22,"tag":896,"props":1169,"children":1170},{"style":909},[1171],{"type":27,"value":912},{"type":22,"tag":896,"props":1173,"children":1174},{"style":903},[1175],{"type":27,"value":917},{"type":22,"tag":23,"props":1177,"children":1178},{},[1179],{"type":27,"value":1180},"Or it could be defined in terms of \"semantic\" HTML elements:",{"type":22,"tag":886,"props":1182,"children":1184},{"className":888,"code":1183,"language":890,"meta":8,"style":8},"\u003Cbody>\n    \u003Caside class=\"navbar\">\n        \u003Cimg class=\"logo\">\n        \u003Cnav>Navigation links\u003C/nav>\n    \u003C/aside>\n    \u003Cmain>Main page content\u003C/main>\n    \u003Cfooter>Page footer\u003C/footer>\n\u003C/body>\n",[1185],{"type":22,"tag":486,"props":1186,"children":1187},{"__ignoreMap":8},[1188,1203,1231,1258,1282,1297,1321,1345],{"type":22,"tag":896,"props":1189,"children":1190},{"class":898,"line":899},[1191,1195,1199],{"type":22,"tag":896,"props":1192,"children":1193},{"style":903},[1194],{"type":27,"value":906},{"type":22,"tag":896,"props":1196,"children":1197},{"style":909},[1198],{"type":27,"value":912},{"type":22,"tag":896,"props":1200,"children":1201},{"style":903},[1202],{"type":27,"value":917},{"type":22,"tag":896,"props":1204,"children":1205},{"class":898,"line":758},[1206,1210,1215,1219,1223,1227],{"type":22,"tag":896,"props":1207,"children":1208},{"style":903},[1209],{"type":27,"value":925},{"type":22,"tag":896,"props":1211,"children":1212},{"style":909},[1213],{"type":27,"value":1214},"aside",{"type":22,"tag":896,"props":1216,"children":1217},{"style":933},[1218],{"type":27,"value":936},{"type":22,"tag":896,"props":1220,"children":1221},{"style":903},[1222],{"type":27,"value":941},{"type":22,"tag":896,"props":1224,"children":1225},{"style":944},[1226],{"type":27,"value":947},{"type":22,"tag":896,"props":1228,"children":1229},{"style":903},[1230],{"type":27,"value":917},{"type":22,"tag":896,"props":1232,"children":1233},{"class":898,"line":755},[1234,1238,1242,1246,1250,1254],{"type":22,"tag":896,"props":1235,"children":1236},{"style":903},[1237],{"type":27,"value":959},{"type":22,"tag":896,"props":1239,"children":1240},{"style":909},[1241],{"type":27,"value":314},{"type":22,"tag":896,"props":1243,"children":1244},{"style":933},[1245],{"type":27,"value":936},{"type":22,"tag":896,"props":1247,"children":1248},{"style":903},[1249],{"type":27,"value":941},{"type":22,"tag":896,"props":1251,"children":1252},{"style":944},[1253],{"type":27,"value":1006},{"type":22,"tag":896,"props":1255,"children":1256},{"style":903},[1257],{"type":27,"value":917},{"type":22,"tag":896,"props":1259,"children":1260},{"class":898,"line":983},[1261,1265,1270,1274,1278],{"type":22,"tag":896,"props":1262,"children":1263},{"style":903},[1264],{"type":27,"value":959},{"type":22,"tag":896,"props":1266,"children":1267},{"style":909},[1268],{"type":27,"value":1269},"nav",{"type":22,"tag":896,"props":1271,"children":1272},{"style":903},[1273],{"type":27,"value":1057},{"type":22,"tag":896,"props":1275,"children":1276},{"style":909},[1277],{"type":27,"value":1269},{"type":22,"tag":896,"props":1279,"children":1280},{"style":903},[1281],{"type":27,"value":917},{"type":22,"tag":896,"props":1283,"children":1284},{"class":898,"line":1013},[1285,1289,1293],{"type":22,"tag":896,"props":1286,"children":1287},{"style":903},[1288],{"type":27,"value":1074},{"type":22,"tag":896,"props":1290,"children":1291},{"style":909},[1292],{"type":27,"value":1214},{"type":22,"tag":896,"props":1294,"children":1295},{"style":903},[1296],{"type":27,"value":917},{"type":22,"tag":896,"props":1298,"children":1299},{"class":898,"line":1030},[1300,1304,1309,1313,1317],{"type":22,"tag":896,"props":1301,"children":1302},{"style":903},[1303],{"type":27,"value":925},{"type":22,"tag":896,"props":1305,"children":1306},{"style":909},[1307],{"type":27,"value":1308},"main",{"type":22,"tag":896,"props":1310,"children":1311},{"style":903},[1312],{"type":27,"value":1112},{"type":22,"tag":896,"props":1314,"children":1315},{"style":909},[1316],{"type":27,"value":1308},{"type":22,"tag":896,"props":1318,"children":1319},{"style":903},[1320],{"type":27,"value":917},{"type":22,"tag":896,"props":1322,"children":1323},{"class":898,"line":1068},[1324,1328,1333,1337,1341],{"type":22,"tag":896,"props":1325,"children":1326},{"style":903},[1327],{"type":27,"value":925},{"type":22,"tag":896,"props":1329,"children":1330},{"style":909},[1331],{"type":27,"value":1332},"footer",{"type":22,"tag":896,"props":1334,"children":1335},{"style":903},[1336],{"type":27,"value":1150},{"type":22,"tag":896,"props":1338,"children":1339},{"style":909},[1340],{"type":27,"value":1332},{"type":22,"tag":896,"props":1342,"children":1343},{"style":903},[1344],{"type":27,"value":917},{"type":22,"tag":896,"props":1346,"children":1347},{"class":898,"line":1085},[1348,1352,1356],{"type":22,"tag":896,"props":1349,"children":1350},{"style":903},[1351],{"type":27,"value":1167},{"type":22,"tag":896,"props":1353,"children":1354},{"style":909},[1355],{"type":27,"value":912},{"type":22,"tag":896,"props":1357,"children":1358},{"style":903},[1359],{"type":27,"value":917},{"type":22,"tag":23,"props":1361,"children":1362},{},[1363,1365,1370,1372,1378],{"type":27,"value":1364},"Each could be styled identically through CSS, so the rendered end product is the same, but the semantic HTML document encodes more information; a screen reader for example could identify the main page content and the navigation area. It also drops the ",{"type":22,"tag":486,"props":1366,"children":1368},{"className":1367},[],[1369],{"type":27,"value":882},{"type":27,"value":1371}," around the ",{"type":22,"tag":486,"props":1373,"children":1375},{"className":1374},[],[1376],{"type":27,"value":1377},"\u003Cimg>",{"type":27,"value":1379}," tag that was only used for styling, which was cluttering up the document.",{"type":22,"tag":193,"props":1381,"children":1383},{"id":1382},"why-semantic-html",[1384],{"type":27,"value":1385},"Why Semantic HTML?",{"type":22,"tag":23,"props":1387,"children":1388},{},[1389],{"type":27,"value":1390},"First off, a semantic HTML document is one that is inherently more accessible to software, because the meaning of page elements is encoded in the document itself. This means better accessibility to screen readers, and improvements to search engine optimization.",{"type":22,"tag":23,"props":1392,"children":1393},{},[1394],{"type":27,"value":1395},"Second, semantic HTML facilitates code reuse when building layouts that are responsive to different page sizes. Too often, I see pages which conditionally render different HTML content depending on device width; not only does this make for a less performant page, but it also creates a source of bugs, since often this creates a lot of duplication.",{"type":22,"tag":23,"props":1397,"children":1398},{},[1399,1401,1406],{"type":27,"value":1400},"Finally, I believe that writing semantic HTML makes for more robust and maintanable code, since it forces the developer to really think hard about the content that exists on the page and how it all logically relates. When developers feel free to arbitrarily use ",{"type":22,"tag":486,"props":1402,"children":1404},{"className":1403},[],[1405],{"type":27,"value":882},{"type":27,"value":1407}," elements to nest content in a way that's more convenient to style, it creates pages that are brittle in the face of change. They're also harder for developers to maintain, because the HTML is difficult to navigate when developing and debugging.",{"type":22,"tag":23,"props":1409,"children":1410},{},[1411],{"type":27,"value":1412},"If semantic HTML is so great, then I suppose it's fair to ask:",{"type":22,"tag":193,"props":1414,"children":1416},{"id":1415},"why-isnt-semantic-html-everywhere",[1417],{"type":27,"value":1418},"Why Isn't Semantic HTML Everywhere?",{"type":22,"tag":23,"props":1420,"children":1421},{},[1422],{"type":27,"value":1423},"And why do frameworks like Tailwind exist which unabashedly paint it as an unattainable goal?",{"type":22,"tag":23,"props":1425,"children":1426},{},[1427],{"type":27,"value":1428},"I think the answer is that historically, they've been right.",{"type":22,"tag":23,"props":1430,"children":1431},{},[1432,1434,1440,1442,1448,1450,1455],{"type":27,"value":1433},"Up until relatively recently if you wanted to, say, have a single HTML document and style it both with a sidebar on the left for desktop and a navbar on the top for mobile, you would have had a difficult time. There was ",{"type":22,"tag":486,"props":1435,"children":1437},{"className":1436},[],[1438],{"type":27,"value":1439},"float",{"type":27,"value":1441}," and ",{"type":22,"tag":486,"props":1443,"children":1445},{"className":1444},[],[1446],{"type":27,"value":1447},"position: absolute",{"type":27,"value":1449}," for addressing these layout challenges, but the former is limited and the latter typically requires hardcoded dimensions and positions which create maintainability issues. There also haven't always been so many semantic HTML elements, leaving more to be defined with ",{"type":22,"tag":486,"props":1451,"children":1453},{"className":1452},[],[1454],{"type":27,"value":882},{"type":27,"value":1456}," tags.",{"type":22,"tag":23,"props":1458,"children":1459},{},[1460,1462,1467],{"type":27,"value":1461},"Under all those limitations, it ",{"type":22,"tag":267,"props":1463,"children":1464},{},[1465],{"type":27,"value":1466},"was",{"type":27,"value":1468}," hard to write semantic HTML documents for complex webpages that were also functional and maintainable. Since the late 2010's however, a few key CSS features found widespread browser support, which allow for completely re-arranging elements on the page: CSS Flexbox and Grid. Since then, I think it has been perfectly achievable to keep style concerns out of HTML documents and reap the benefits.",{"type":22,"tag":193,"props":1470,"children":1472},{"id":1471},"re-arranging-a-page-with-css-grid-and-flexbox",[1473],{"type":27,"value":1474},"Re-arranging a page with CSS Grid and Flexbox",{"type":22,"tag":23,"props":1476,"children":1477},{},[1478],{"type":27,"value":1479},"With these tools in hand, an HTML document can be written in a sensible structure for sequential reading (e.g. by screen reader or search engine) and then be displayed with a huge amount of flexibility. Here's a quick overview of the techniques I've found the most helpful to that end:",{"type":22,"tag":1481,"props":1482,"children":1484},"h3",{"id":1483},"reordering-elements-with-display-flex-and-order",[1485,1487,1493,1494],{"type":27,"value":1486},"Reordering elements with ",{"type":22,"tag":486,"props":1488,"children":1490},{"className":1489},[],[1491],{"type":27,"value":1492},"display: flex",{"type":27,"value":1441},{"type":22,"tag":486,"props":1495,"children":1497},{"className":1496},[],[1498],{"type":27,"value":1499},"order",{"type":22,"tag":23,"props":1501,"children":1502},{},[1503,1505,1515,1517,1528,1530,1536,1537,1543],{"type":27,"value":1504},"One easy trick, when displaying elements in a flexible row or column Flexbox, is that the child elements can accept an integral ",{"type":22,"tag":30,"props":1506,"children":1509},{"href":1507,"rel":1508},"https://css-tricks.com/almanac/properties/o/order/",[34],[1510],{"type":22,"tag":486,"props":1511,"children":1513},{"className":1512},[],[1514],{"type":27,"value":1499},{"type":27,"value":1516}," property which defines their order in the row/column. And although it's a little more situational, keep in mind that ",{"type":22,"tag":30,"props":1518,"children":1521},{"href":1519,"rel":1520},"https://css-tricks.com/almanac/properties/f/flex-direction/",[34],[1522],{"type":22,"tag":486,"props":1523,"children":1525},{"className":1524},[],[1526],{"type":27,"value":1527},"flex-direction",{"type":27,"value":1529}," also has ",{"type":22,"tag":486,"props":1531,"children":1533},{"className":1532},[],[1534],{"type":27,"value":1535},"row-reverse",{"type":27,"value":1441},{"type":22,"tag":486,"props":1538,"children":1540},{"className":1539},[],[1541],{"type":27,"value":1542},"column-reverse",{"type":27,"value":1544}," values to reverse the order of all elements in the container.",{"type":22,"tag":23,"props":1546,"children":1547},{},[1548,1550,1556,1558,1564],{"type":27,"value":1549},"An example use case might be a navbar which on desktop displays as a sidebar (",{"type":22,"tag":486,"props":1551,"children":1553},{"className":1552},[],[1554],{"type":27,"value":1555},"flex-direction: column",{"type":27,"value":1557},") and orders its elements \"title, logo, navigation\", and on mobile displays as a top bar (",{"type":22,"tag":486,"props":1559,"children":1561},{"className":1560},[],[1562],{"type":27,"value":1563},"flex-direction: row",{"type":27,"value":1565},") and orders its elements \"logo, title, navigation\".",{"type":22,"tag":1481,"props":1567,"children":1569},{"id":1568},"positioning-elements-with-display-grid",[1570,1572],{"type":27,"value":1571},"Positioning elements with ",{"type":22,"tag":486,"props":1573,"children":1575},{"className":1574},[],[1576],{"type":27,"value":1577},"display: grid",{"type":22,"tag":23,"props":1579,"children":1580},{},[1581,1583,1589,1591,1598],{"type":27,"value":1582},"This, in my opinion, is the big game-changer: CSS Grid allows elements to be positioned on a grid in a very flexible way. The ",{"type":22,"tag":486,"props":1584,"children":1586},{"className":1585},[],[1587],{"type":27,"value":1588},"grid",{"type":27,"value":1590}," property, while a little overwhelming when first learning it (see CSS-Tricks's ",{"type":22,"tag":30,"props":1592,"children":1595},{"href":1593,"rel":1594},"https://css-tricks.com/snippets/css/complete-guide-grid/",[34],[1596],{"type":27,"value":1597},"Complete Guide to CSS Grid",{"type":27,"value":1599},"), provides a very visual way to set up the grid and assign elements to it.",{"type":22,"tag":23,"props":1601,"children":1602},{},[1603],{"type":27,"value":1604},"In the demo application for example, the \"Desktop\" layout uses the following grid value:",{"type":22,"tag":886,"props":1606,"children":1610},{"className":1607,"code":1608,"language":1609,"meta":8,"style":8},"language-css shiki shiki-themes github-light github-dark",".app-desktop {\n    display: grid;\n    grid:\n        \"header main\" auto\n        \"navbar main\" 1fr\n        \"footer main\" auto\n        / 288px 1fr;\n}\n","css",[1611],{"type":22,"tag":486,"props":1612,"children":1613},{"__ignoreMap":8},[1614,1627,1650,1663,1676,1695,1707,1738],{"type":22,"tag":896,"props":1615,"children":1616},{"class":898,"line":899},[1617,1622],{"type":22,"tag":896,"props":1618,"children":1619},{"style":933},[1620],{"type":27,"value":1621},".app-desktop",{"type":22,"tag":896,"props":1623,"children":1624},{"style":903},[1625],{"type":27,"value":1626}," {\n",{"type":22,"tag":896,"props":1628,"children":1629},{"class":898,"line":758},[1630,1636,1641,1645],{"type":22,"tag":896,"props":1631,"children":1633},{"style":1632},"--shiki-default:#005CC5;--shiki-dark:#79B8FF",[1634],{"type":27,"value":1635},"    display",{"type":22,"tag":896,"props":1637,"children":1638},{"style":903},[1639],{"type":27,"value":1640},": ",{"type":22,"tag":896,"props":1642,"children":1643},{"style":1632},[1644],{"type":27,"value":1588},{"type":22,"tag":896,"props":1646,"children":1647},{"style":903},[1648],{"type":27,"value":1649},";\n",{"type":22,"tag":896,"props":1651,"children":1652},{"class":898,"line":755},[1653,1658],{"type":22,"tag":896,"props":1654,"children":1655},{"style":1632},[1656],{"type":27,"value":1657},"    grid",{"type":22,"tag":896,"props":1659,"children":1660},{"style":903},[1661],{"type":27,"value":1662},":\n",{"type":22,"tag":896,"props":1664,"children":1665},{"class":898,"line":983},[1666,1671],{"type":22,"tag":896,"props":1667,"children":1668},{"style":944},[1669],{"type":27,"value":1670},"        \"header main\"",{"type":22,"tag":896,"props":1672,"children":1673},{"style":1632},[1674],{"type":27,"value":1675}," auto\n",{"type":22,"tag":896,"props":1677,"children":1678},{"class":898,"line":1013},[1679,1684,1689],{"type":22,"tag":896,"props":1680,"children":1681},{"style":944},[1682],{"type":27,"value":1683},"        \"navbar main\"",{"type":22,"tag":896,"props":1685,"children":1686},{"style":1632},[1687],{"type":27,"value":1688}," 1",{"type":22,"tag":896,"props":1690,"children":1692},{"style":1691},"--shiki-default:#D73A49;--shiki-dark:#F97583",[1693],{"type":27,"value":1694},"fr\n",{"type":22,"tag":896,"props":1696,"children":1697},{"class":898,"line":1030},[1698,1703],{"type":22,"tag":896,"props":1699,"children":1700},{"style":944},[1701],{"type":27,"value":1702},"        \"footer main\"",{"type":22,"tag":896,"props":1704,"children":1705},{"style":1632},[1706],{"type":27,"value":1675},{"type":22,"tag":896,"props":1708,"children":1709},{"class":898,"line":1068},[1710,1715,1720,1725,1729,1734],{"type":22,"tag":896,"props":1711,"children":1712},{"style":903},[1713],{"type":27,"value":1714},"        / ",{"type":22,"tag":896,"props":1716,"children":1717},{"style":1632},[1718],{"type":27,"value":1719},"288",{"type":22,"tag":896,"props":1721,"children":1722},{"style":1691},[1723],{"type":27,"value":1724},"px",{"type":22,"tag":896,"props":1726,"children":1727},{"style":1632},[1728],{"type":27,"value":1688},{"type":22,"tag":896,"props":1730,"children":1731},{"style":1691},[1732],{"type":27,"value":1733},"fr",{"type":22,"tag":896,"props":1735,"children":1736},{"style":903},[1737],{"type":27,"value":1649},{"type":22,"tag":896,"props":1739,"children":1740},{"class":898,"line":1085},[1741],{"type":22,"tag":896,"props":1742,"children":1743},{"style":903},[1744],{"type":27,"value":1745},"}\n",{"type":22,"tag":23,"props":1747,"children":1748},{},[1749,1751,1757,1759,1765,1767,1772,1774,1779,1781,1787],{"type":27,"value":1750},"Without going too deep into the details, what this represents is a 2x3 grid with a ",{"type":22,"tag":486,"props":1752,"children":1754},{"className":1753},[],[1755],{"type":27,"value":1756},"header",{"type":27,"value":1758}," area in the top-left, a ",{"type":22,"tag":486,"props":1760,"children":1762},{"className":1761},[],[1763],{"type":27,"value":1764},"navbar",{"type":27,"value":1766}," area in the middle-left, a ",{"type":22,"tag":486,"props":1768,"children":1770},{"className":1769},[],[1771],{"type":27,"value":1332},{"type":27,"value":1773}," area in the bottom-left, and ",{"type":22,"tag":486,"props":1775,"children":1777},{"className":1776},[],[1778],{"type":27,"value":1308},{"type":27,"value":1780}," area stretching down the whole right side. The lengths at the end of each line are the row heights, and the lengths on the last line are the column widths. With each element assigned to a grid area (e.g. with ",{"type":22,"tag":486,"props":1782,"children":1784},{"className":1783},[],[1785],{"type":27,"value":1786},"grid-area: header",{"type":27,"value":1788},"), they'll appear in the appropriate grid area regardless of their order in the document.",{"type":22,"tag":23,"props":1790,"children":1791},{},[1792,1794,1801],{"type":27,"value":1793},"This is how it looks in Chrome with its ",{"type":22,"tag":30,"props":1795,"children":1798},{"href":1796,"rel":1797},"https://developer.chrome.com/docs/devtools/css/grid/",[34],[1799],{"type":27,"value":1800},"CSS Grid debugging tools",{"type":27,"value":1802}," enabled:",{"type":22,"tag":23,"props":1804,"children":1805},{},[1806],{"type":22,"tag":314,"props":1807,"children":1810},{"alt":1808,"src":1809},"The demo \"Desktop\" layout, with Chrome debugging applied to identify the grid areas","/phendry/2023-04-02/img/grid_debug_desktop.png",[],{"type":22,"tag":23,"props":1812,"children":1813},{},[1814],{"type":27,"value":1815},"The orange lines show the grid cells (where solid lines denote the edges of child elements and dashed lines indicate that an element is spanning across multiple cells), and the orange labels show the names of the grid areas. Despite the header, footer and navbar elements all being distinct, we can use a grid to easily style them so that they all look like a unified sidebar.",{"type":22,"tag":23,"props":1817,"children":1818},{},[1819],{"type":27,"value":1820},"The \"Mobile\" layout takes the same document and places it on a totally different grid:",{"type":22,"tag":886,"props":1822,"children":1824},{"className":1607,"code":1823,"language":1609,"meta":8,"style":8},".app-mobile {\n    display: grid;\n    grid:\n        \"logo   header header nav\"   64px\n        \"main   main   main   main\"  1fr\n        \"footer footer style  style\" auto\n        / auto 1fr auto auto;\n}\n",[1825],{"type":22,"tag":486,"props":1826,"children":1827},{"__ignoreMap":8},[1828,1840,1859,1870,1888,1905,1917,1950],{"type":22,"tag":896,"props":1829,"children":1830},{"class":898,"line":899},[1831,1836],{"type":22,"tag":896,"props":1832,"children":1833},{"style":933},[1834],{"type":27,"value":1835},".app-mobile",{"type":22,"tag":896,"props":1837,"children":1838},{"style":903},[1839],{"type":27,"value":1626},{"type":22,"tag":896,"props":1841,"children":1842},{"class":898,"line":758},[1843,1847,1851,1855],{"type":22,"tag":896,"props":1844,"children":1845},{"style":1632},[1846],{"type":27,"value":1635},{"type":22,"tag":896,"props":1848,"children":1849},{"style":903},[1850],{"type":27,"value":1640},{"type":22,"tag":896,"props":1852,"children":1853},{"style":1632},[1854],{"type":27,"value":1588},{"type":22,"tag":896,"props":1856,"children":1857},{"style":903},[1858],{"type":27,"value":1649},{"type":22,"tag":896,"props":1860,"children":1861},{"class":898,"line":755},[1862,1866],{"type":22,"tag":896,"props":1863,"children":1864},{"style":1632},[1865],{"type":27,"value":1657},{"type":22,"tag":896,"props":1867,"children":1868},{"style":903},[1869],{"type":27,"value":1662},{"type":22,"tag":896,"props":1871,"children":1872},{"class":898,"line":983},[1873,1878,1883],{"type":22,"tag":896,"props":1874,"children":1875},{"style":944},[1876],{"type":27,"value":1877},"        \"logo   header header nav\"",{"type":22,"tag":896,"props":1879,"children":1880},{"style":1632},[1881],{"type":27,"value":1882},"   64",{"type":22,"tag":896,"props":1884,"children":1885},{"style":1691},[1886],{"type":27,"value":1887},"px\n",{"type":22,"tag":896,"props":1889,"children":1890},{"class":898,"line":1013},[1891,1896,1901],{"type":22,"tag":896,"props":1892,"children":1893},{"style":944},[1894],{"type":27,"value":1895},"        \"main   main   main   main\"",{"type":22,"tag":896,"props":1897,"children":1898},{"style":1632},[1899],{"type":27,"value":1900},"  1",{"type":22,"tag":896,"props":1902,"children":1903},{"style":1691},[1904],{"type":27,"value":1694},{"type":22,"tag":896,"props":1906,"children":1907},{"class":898,"line":1030},[1908,1913],{"type":22,"tag":896,"props":1909,"children":1910},{"style":944},[1911],{"type":27,"value":1912},"        \"footer footer style  style\"",{"type":22,"tag":896,"props":1914,"children":1915},{"style":1632},[1916],{"type":27,"value":1675},{"type":22,"tag":896,"props":1918,"children":1919},{"class":898,"line":1068},[1920,1924,1929,1933,1937,1942,1946],{"type":22,"tag":896,"props":1921,"children":1922},{"style":903},[1923],{"type":27,"value":1714},{"type":22,"tag":896,"props":1925,"children":1926},{"style":1632},[1927],{"type":27,"value":1928},"auto",{"type":22,"tag":896,"props":1930,"children":1931},{"style":1632},[1932],{"type":27,"value":1688},{"type":22,"tag":896,"props":1934,"children":1935},{"style":1691},[1936],{"type":27,"value":1733},{"type":22,"tag":896,"props":1938,"children":1939},{"style":1632},[1940],{"type":27,"value":1941}," auto",{"type":22,"tag":896,"props":1943,"children":1944},{"style":1632},[1945],{"type":27,"value":1941},{"type":22,"tag":896,"props":1947,"children":1948},{"style":903},[1949],{"type":27,"value":1649},{"type":22,"tag":896,"props":1951,"children":1952},{"class":898,"line":1085},[1953],{"type":22,"tag":896,"props":1954,"children":1955},{"style":903},[1956],{"type":27,"value":1745},{"type":22,"tag":23,"props":1958,"children":1959},{},[1960,1962,1968,1969,1974,1975,1980,1981,1986,1987,1992,1994,2000],{"type":27,"value":1961},"This is a 4x3 grid, with areas for ",{"type":22,"tag":486,"props":1963,"children":1965},{"className":1964},[],[1966],{"type":27,"value":1967},"logo",{"type":27,"value":236},{"type":22,"tag":486,"props":1970,"children":1972},{"className":1971},[],[1973],{"type":27,"value":1756},{"type":27,"value":236},{"type":22,"tag":486,"props":1976,"children":1978},{"className":1977},[],[1979],{"type":27,"value":1269},{"type":27,"value":236},{"type":22,"tag":486,"props":1982,"children":1984},{"className":1983},[],[1985],{"type":27,"value":1308},{"type":27,"value":236},{"type":22,"tag":486,"props":1988,"children":1990},{"className":1989},[],[1991],{"type":27,"value":1332},{"type":27,"value":1993},", and ",{"type":22,"tag":486,"props":1995,"children":1997},{"className":1996},[],[1998],{"type":27,"value":1999},"style",{"type":27,"value":2001}," (the dropdown used to change layouts). By having four columns, the elements of the header and the footer don't need to be in alignment, allowing each to flow as expected, best illustrated in the debugging screenshot below:",{"type":22,"tag":23,"props":2003,"children":2004},{},[2005],{"type":22,"tag":314,"props":2006,"children":2009},{"alt":2007,"src":2008},"The demo \"Mobile\" layout, with Chrome debugging applied to identify the grid areas","/phendry/2023-04-02/img/grid_debug_mobile.png",[],{"type":22,"tag":23,"props":2011,"children":2012},{},[2013],{"type":27,"value":2014},"These two pages have almost nothing in common layout-wise, but they share the same HTML document, and CSS Grid is doing the leg work.",{"type":22,"tag":1481,"props":2016,"children":2018},{"id":2017},"collapsing-elements-with-display-contents",[2019,2021],{"type":27,"value":2020},"Collapsing elements with ",{"type":22,"tag":486,"props":2022,"children":2024},{"className":2023},[],[2025],{"type":27,"value":2026},"display: contents",{"type":22,"tag":23,"props":2028,"children":2029},{},[2030],{"type":27,"value":2031},"The \"Mobile\" layout above has one trick which is normally not allowed in CSS Grids: it puts two elements on the grid that are not direct children of the grid element:",{"type":22,"tag":886,"props":2033,"children":2035},{"className":888,"code":2034,"language":890,"meta":8,"style":8},"\u003Cdiv class=\"app\"> \u003C!-- Grid parent -->\n    \u003Cheader>...\u003C/header> \u003C!-- Child -->\n    \u003Caside class=\"navbar\">\n        ...\n        \u003Cnav class=\"navbar__nav\">...\u003C/nav> \u003C!-- NOT a child -->\n        \u003Cform class=\"navbar__form-options\">...\u003C/form> \u003C!-- NOT a child -->\n        ...\n    \u003C/aside>\n    \u003Cmain>...\u003C/main> \u003C!-- Child -->\n    \u003Cfooter>...\u003C/footer> \u003C!-- Child -->\n\u003C/div>\n",[2036],{"type":22,"tag":486,"props":2037,"children":2038},{"__ignoreMap":8},[2039,2074,2103,2130,2138,2179,2220,2227,2242,2269,2296],{"type":22,"tag":896,"props":2040,"children":2041},{"class":898,"line":899},[2042,2046,2050,2054,2058,2063,2068],{"type":22,"tag":896,"props":2043,"children":2044},{"style":903},[2045],{"type":27,"value":906},{"type":22,"tag":896,"props":2047,"children":2048},{"style":909},[2049],{"type":27,"value":930},{"type":22,"tag":896,"props":2051,"children":2052},{"style":933},[2053],{"type":27,"value":936},{"type":22,"tag":896,"props":2055,"children":2056},{"style":903},[2057],{"type":27,"value":941},{"type":22,"tag":896,"props":2059,"children":2060},{"style":944},[2061],{"type":27,"value":2062},"\"app\"",{"type":22,"tag":896,"props":2064,"children":2065},{"style":903},[2066],{"type":27,"value":2067},"> ",{"type":22,"tag":896,"props":2069,"children":2071},{"style":2070},"--shiki-default:#6A737D;--shiki-dark:#6A737D",[2072],{"type":27,"value":2073},"\u003C!-- Grid parent -->\n",{"type":22,"tag":896,"props":2075,"children":2076},{"class":898,"line":758},[2077,2081,2085,2090,2094,2098],{"type":22,"tag":896,"props":2078,"children":2079},{"style":903},[2080],{"type":27,"value":925},{"type":22,"tag":896,"props":2082,"children":2083},{"style":909},[2084],{"type":27,"value":1756},{"type":22,"tag":896,"props":2086,"children":2087},{"style":903},[2088],{"type":27,"value":2089},">...\u003C/",{"type":22,"tag":896,"props":2091,"children":2092},{"style":909},[2093],{"type":27,"value":1756},{"type":22,"tag":896,"props":2095,"children":2096},{"style":903},[2097],{"type":27,"value":2067},{"type":22,"tag":896,"props":2099,"children":2100},{"style":2070},[2101],{"type":27,"value":2102},"\u003C!-- Child -->\n",{"type":22,"tag":896,"props":2104,"children":2105},{"class":898,"line":755},[2106,2110,2114,2118,2122,2126],{"type":22,"tag":896,"props":2107,"children":2108},{"style":903},[2109],{"type":27,"value":925},{"type":22,"tag":896,"props":2111,"children":2112},{"style":909},[2113],{"type":27,"value":1214},{"type":22,"tag":896,"props":2115,"children":2116},{"style":933},[2117],{"type":27,"value":936},{"type":22,"tag":896,"props":2119,"children":2120},{"style":903},[2121],{"type":27,"value":941},{"type":22,"tag":896,"props":2123,"children":2124},{"style":944},[2125],{"type":27,"value":947},{"type":22,"tag":896,"props":2127,"children":2128},{"style":903},[2129],{"type":27,"value":917},{"type":22,"tag":896,"props":2131,"children":2132},{"class":898,"line":983},[2133],{"type":22,"tag":896,"props":2134,"children":2135},{"style":903},[2136],{"type":27,"value":2137},"        ...\n",{"type":22,"tag":896,"props":2139,"children":2140},{"class":898,"line":1013},[2141,2145,2149,2153,2157,2162,2166,2170,2174],{"type":22,"tag":896,"props":2142,"children":2143},{"style":903},[2144],{"type":27,"value":959},{"type":22,"tag":896,"props":2146,"children":2147},{"style":909},[2148],{"type":27,"value":1269},{"type":22,"tag":896,"props":2150,"children":2151},{"style":933},[2152],{"type":27,"value":936},{"type":22,"tag":896,"props":2154,"children":2155},{"style":903},[2156],{"type":27,"value":941},{"type":22,"tag":896,"props":2158,"children":2159},{"style":944},[2160],{"type":27,"value":2161},"\"navbar__nav\"",{"type":22,"tag":896,"props":2163,"children":2164},{"style":903},[2165],{"type":27,"value":2089},{"type":22,"tag":896,"props":2167,"children":2168},{"style":909},[2169],{"type":27,"value":1269},{"type":22,"tag":896,"props":2171,"children":2172},{"style":903},[2173],{"type":27,"value":2067},{"type":22,"tag":896,"props":2175,"children":2176},{"style":2070},[2177],{"type":27,"value":2178},"\u003C!-- NOT a child -->\n",{"type":22,"tag":896,"props":2180,"children":2181},{"class":898,"line":1030},[2182,2186,2191,2195,2199,2204,2208,2212,2216],{"type":22,"tag":896,"props":2183,"children":2184},{"style":903},[2185],{"type":27,"value":959},{"type":22,"tag":896,"props":2187,"children":2188},{"style":909},[2189],{"type":27,"value":2190},"form",{"type":22,"tag":896,"props":2192,"children":2193},{"style":933},[2194],{"type":27,"value":936},{"type":22,"tag":896,"props":2196,"children":2197},{"style":903},[2198],{"type":27,"value":941},{"type":22,"tag":896,"props":2200,"children":2201},{"style":944},[2202],{"type":27,"value":2203},"\"navbar__form-options\"",{"type":22,"tag":896,"props":2205,"children":2206},{"style":903},[2207],{"type":27,"value":2089},{"type":22,"tag":896,"props":2209,"children":2210},{"style":909},[2211],{"type":27,"value":2190},{"type":22,"tag":896,"props":2213,"children":2214},{"style":903},[2215],{"type":27,"value":2067},{"type":22,"tag":896,"props":2217,"children":2218},{"style":2070},[2219],{"type":27,"value":2178},{"type":22,"tag":896,"props":2221,"children":2222},{"class":898,"line":1068},[2223],{"type":22,"tag":896,"props":2224,"children":2225},{"style":903},[2226],{"type":27,"value":2137},{"type":22,"tag":896,"props":2228,"children":2229},{"class":898,"line":1085},[2230,2234,2238],{"type":22,"tag":896,"props":2231,"children":2232},{"style":903},[2233],{"type":27,"value":1074},{"type":22,"tag":896,"props":2235,"children":2236},{"style":909},[2237],{"type":27,"value":1214},{"type":22,"tag":896,"props":2239,"children":2240},{"style":903},[2241],{"type":27,"value":917},{"type":22,"tag":896,"props":2243,"children":2244},{"class":898,"line":1123},[2245,2249,2253,2257,2261,2265],{"type":22,"tag":896,"props":2246,"children":2247},{"style":903},[2248],{"type":27,"value":925},{"type":22,"tag":896,"props":2250,"children":2251},{"style":909},[2252],{"type":27,"value":1308},{"type":22,"tag":896,"props":2254,"children":2255},{"style":903},[2256],{"type":27,"value":2089},{"type":22,"tag":896,"props":2258,"children":2259},{"style":909},[2260],{"type":27,"value":1308},{"type":22,"tag":896,"props":2262,"children":2263},{"style":903},[2264],{"type":27,"value":2067},{"type":22,"tag":896,"props":2266,"children":2267},{"style":2070},[2268],{"type":27,"value":2102},{"type":22,"tag":896,"props":2270,"children":2271},{"class":898,"line":1161},[2272,2276,2280,2284,2288,2292],{"type":22,"tag":896,"props":2273,"children":2274},{"style":903},[2275],{"type":27,"value":925},{"type":22,"tag":896,"props":2277,"children":2278},{"style":909},[2279],{"type":27,"value":1332},{"type":22,"tag":896,"props":2281,"children":2282},{"style":903},[2283],{"type":27,"value":2089},{"type":22,"tag":896,"props":2285,"children":2286},{"style":909},[2287],{"type":27,"value":1332},{"type":22,"tag":896,"props":2289,"children":2290},{"style":903},[2291],{"type":27,"value":2067},{"type":22,"tag":896,"props":2293,"children":2294},{"style":2070},[2295],{"type":27,"value":2102},{"type":22,"tag":896,"props":2297,"children":2299},{"class":898,"line":2298},11,[2300,2304,2308],{"type":22,"tag":896,"props":2301,"children":2302},{"style":903},[2303],{"type":27,"value":1167},{"type":22,"tag":896,"props":2305,"children":2306},{"style":909},[2307],{"type":27,"value":930},{"type":22,"tag":896,"props":2309,"children":2310},{"style":903},[2311],{"type":27,"value":917},{"type":22,"tag":23,"props":2313,"children":2314},{},[2315,2317,2322,2324,2330,2332,2338,2339,2345,2347,2352,2354,2359],{"type":27,"value":2316},"Thankfully, another CSS property is helpful here: ",{"type":22,"tag":486,"props":2318,"children":2320},{"className":2319},[],[2321],{"type":27,"value":2026},{"type":27,"value":2323},". When applied to an element, it essentially makes it disappear and have its children take its place, as far as rendering is concerned. The \"Mobile\" layout applies this to the ",{"type":22,"tag":486,"props":2325,"children":2327},{"className":2326},[],[2328],{"type":27,"value":2329},"\u003Caside>",{"type":27,"value":2331}," element, causing the ",{"type":22,"tag":486,"props":2333,"children":2335},{"className":2334},[],[2336],{"type":27,"value":2337},"\u003Cnav>",{"type":27,"value":1441},{"type":22,"tag":486,"props":2340,"children":2342},{"className":2341},[],[2343],{"type":27,"value":2344},"\u003Cform>",{"type":27,"value":2346}," elements to behave as if they were children of the ",{"type":22,"tag":486,"props":2348,"children":2350},{"className":2349},[],[2351],{"type":27,"value":882},{"type":27,"value":2353},", and making them valid elements to place on the ",{"type":22,"tag":486,"props":2355,"children":2357},{"className":2356},[],[2358],{"type":27,"value":882},{"type":27,"value":2360},"'s Grid.",{"type":22,"tag":23,"props":2362,"children":2363},{},[2364],{"type":27,"value":2365},"This is a great way to retain the capabilities of CSS Grid when writing semantic HTML, because elements can be defined in a sensible nested structure while still being valid targets for Grid-based styling.",{"type":22,"tag":1481,"props":2367,"children":2369},{"id":2368},"css-based-dropdowns-using-position-absolute-and-hover",[2370,2372,2377,2378],{"type":27,"value":2371},"CSS-based dropdowns using ",{"type":22,"tag":486,"props":2373,"children":2375},{"className":2374},[],[2376],{"type":27,"value":1447},{"type":27,"value":1441},{"type":22,"tag":486,"props":2379,"children":2381},{"className":2380},[],[2382],{"type":27,"value":2383},":hover",{"type":22,"tag":23,"props":2385,"children":2386},{},[2387],{"type":27,"value":2388},"While it has limitations that mean it won't work for all scenarios, a traditional navigation menu can also be converted into a keyboard- and mobile-friendly dropdown menu purely with CSS. The demo project does this in its \"Mobile\" layout:",{"type":22,"tag":23,"props":2390,"children":2391},{},[2392],{"type":22,"tag":314,"props":2393,"children":2396},{"alt":2394,"src":2395},"The dropdown menu in the \"Mobile\" layout of the demo project","/phendry/2023-04-02/img/demo_dropdown.png",[],{"type":22,"tag":23,"props":2398,"children":2399},{},[2400,2402,2409,2411,2416,2417,2423],{"type":27,"value":2401},"The details are a little too detailed to cover here so I'll defer to ",{"type":22,"tag":30,"props":2403,"children":2406},{"href":2404,"rel":2405},"https://css-tricks.com/solved-with-css-dropdown-menus/",[34],[2407],{"type":27,"value":2408},"this CSS-Tricks article",{"type":27,"value":2410}," that covers it, but essentially all it's using are the ",{"type":22,"tag":486,"props":2412,"children":2414},{"className":2413},[],[2415],{"type":27,"value":2383},{"type":27,"value":1441},{"type":22,"tag":486,"props":2418,"children":2420},{"className":2419},[],[2421],{"type":27,"value":2422},":focus-within",{"type":27,"value":2424}," pseudo-classes to control when the dropdown container and mouseover effects are displayed.",{"type":22,"tag":193,"props":2426,"children":2428},{"id":2427},"give-semantic-html-a-chance",[2429],{"type":27,"value":2430},"Give Semantic HTML a Chance!",{"type":22,"tag":23,"props":2432,"children":2433},{},[2434,2436,2441],{"type":27,"value":2435},"Tailwind and other \"atomic CSS\" frameworks are perfectly capable tools and many developers love them, but I can't help but feel that they don't so much solve a problem as much as they ",{"type":22,"tag":267,"props":2437,"children":2438},{},[2439],{"type":27,"value":2440},"shift",{"type":27,"value":2442}," it elsewhere. Defining presentation inline in HTML solves the problem of unmaintainable CSS files, but it also creates a problem of duplication (whenever multiple layouts need to be implemented, at least). Some teams may be better equipped to handle the problems of atomic CSS compared to those of semantic HTML/CSS, but I'm not convinced that they're fundamentally easier to overcome.",{"type":22,"tag":23,"props":2444,"children":2445},{},[2446],{"type":27,"value":2447},"On the contrary, the problems with atomic CSS are arguably inherent to the technique, whereas problems of semantic CSS maintainability can be addressed with largely the same techniques used for code maintainability. As challenging as it can be to maintain CSS, I think most teams would be better off addressing the root causes that are leading to their CSS maintainability issues, rather than reach for a technical solution to what is ultimately (in my view) a knowledge and process problem.",{"type":22,"tag":23,"props":2449,"children":2450},{},[2451],{"type":27,"value":2452},"As we've seen here, styling semantic HTML is easier and more powerful than ever, and semantic HTML has a host of benefits that would be a shame to lose out on. So dig into the CSS Grid and Flexbox documentation and give it a go on your current (or next) project!",{"type":22,"tag":1999,"props":2454,"children":2455},{},[2456],{"type":27,"value":2457},"html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":8,"searchDepth":755,"depth":755,"links":2459},[2460,2461,2462,2463,2473],{"id":837,"depth":758,"text":840},{"id":1382,"depth":758,"text":1385},{"id":1415,"depth":758,"text":1418},{"id":1471,"depth":758,"text":1474,"children":2464},[2465,2467,2469,2471],{"id":1483,"depth":755,"text":2466},"Reordering elements with display: flex and order",{"id":1568,"depth":755,"text":2468},"Positioning elements with display: grid",{"id":2017,"depth":755,"text":2470},"Collapsing elements with display: contents",{"id":2368,"depth":755,"text":2472},"CSS-based dropdowns using position: absolute and :hover",{"id":2427,"depth":758,"text":2430},"content:phendry:2023-04-02:SemanticHtml.md","phendry/2023-04-02/SemanticHtml.md","phendry/2023-04-02/SemanticHtml",{"user":774,"name":775},{"_path":2479,"_dir":2480,"_draft":7,"_partial":7,"_locale":8,"title":2481,"description":2482,"image":2483,"publishDate":2484,"tags":2485,"excerpt":2482,"body":2486,"_type":767,"_id":2808,"_source":769,"_file":2809,"_stem":2810,"_extension":772,"author":2811},"/phendry/2023-01-31/forgetaboutcodestyle","2023-01-31","Forget About [Code] Style","Good code style, being highly subjective, is something often debated among developers. After all, we spend more time reading code than writing it, so it's worth making sure our code is styled to be as easy as possible to read and to understand. On the other hand, deciding upon and continuously enforcing a style is also time-consuming, and the benefits are near-impossible to quantify. Given that modern code formatting tools can fully automate the process, is it still worth fretting about style?","/phendry/2023-01-31/img/forget_style_header.png","2024-02-01",[12],{"type":19,"children":2487,"toc":2804},[2488,2492,2498,2503,2540,2545,2586,2591,2654,2659,2665,2670,2750,2755,2768,2773,2778,2783,2795,2800],{"type":22,"tag":23,"props":2489,"children":2490},{},[2491],{"type":27,"value":2482},{"type":22,"tag":193,"props":2493,"children":2495},{"id":2494},"the-hidden-costs-of-being-stylish",[2496],{"type":27,"value":2497},"The Hidden Costs of Being Stylish",{"type":22,"tag":23,"props":2499,"children":2500},{},[2501],{"type":27,"value":2502},"Suppose your team or your company decides that it's more readable to place opening curly braces before the newline, i.e.",{"type":22,"tag":886,"props":2504,"children":2508},{"className":2505,"code":2506,"language":2507,"meta":8,"style":8},"language-js shiki shiki-themes github-light github-dark","if (cond) {\n    ...\n}\n","js",[2509],{"type":22,"tag":486,"props":2510,"children":2511},{"__ignoreMap":8},[2512,2525,2533],{"type":22,"tag":896,"props":2513,"children":2514},{"class":898,"line":899},[2515,2520],{"type":22,"tag":896,"props":2516,"children":2517},{"style":1691},[2518],{"type":27,"value":2519},"if",{"type":22,"tag":896,"props":2521,"children":2522},{"style":903},[2523],{"type":27,"value":2524}," (cond) {\n",{"type":22,"tag":896,"props":2526,"children":2527},{"class":898,"line":758},[2528],{"type":22,"tag":896,"props":2529,"children":2530},{"style":1691},[2531],{"type":27,"value":2532},"    ...\n",{"type":22,"tag":896,"props":2534,"children":2535},{"class":898,"line":755},[2536],{"type":22,"tag":896,"props":2537,"children":2538},{"style":903},[2539],{"type":27,"value":1745},{"type":22,"tag":23,"props":2541,"children":2542},{},[2543],{"type":27,"value":2544},"rather than after it, i.e.",{"type":22,"tag":886,"props":2546,"children":2548},{"className":2505,"code":2547,"language":2507,"meta":8,"style":8},"if (cond)\n{\n    ...\n}\n",[2549],{"type":22,"tag":486,"props":2550,"children":2551},{"__ignoreMap":8},[2552,2564,2572,2579],{"type":22,"tag":896,"props":2553,"children":2554},{"class":898,"line":899},[2555,2559],{"type":22,"tag":896,"props":2556,"children":2557},{"style":1691},[2558],{"type":27,"value":2519},{"type":22,"tag":896,"props":2560,"children":2561},{"style":903},[2562],{"type":27,"value":2563}," (cond)\n",{"type":22,"tag":896,"props":2565,"children":2566},{"class":898,"line":758},[2567],{"type":22,"tag":896,"props":2568,"children":2569},{"style":903},[2570],{"type":27,"value":2571},"{\n",{"type":22,"tag":896,"props":2573,"children":2574},{"class":898,"line":755},[2575],{"type":22,"tag":896,"props":2576,"children":2577},{"style":1691},[2578],{"type":27,"value":2532},{"type":22,"tag":896,"props":2580,"children":2581},{"class":898,"line":983},[2582],{"type":22,"tag":896,"props":2583,"children":2584},{"style":903},[2585],{"type":27,"value":1745},{"type":22,"tag":23,"props":2587,"children":2588},{},[2589],{"type":27,"value":2590},"This small rule carries with it a number of hidden costs:",{"type":22,"tag":847,"props":2592,"children":2593},{},[2594,2614,2624,2634,2644],{"type":22,"tag":115,"props":2595,"children":2596},{},[2597,2603,2605,2612],{"type":22,"tag":2598,"props":2599,"children":2600},"strong",{},[2601],{"type":27,"value":2602},"Defining the style:",{"type":27,"value":2604}," It took at least a few minutes to decide on the style, more if ",{"type":22,"tag":30,"props":2606,"children":2609},{"href":2607,"rel":2608},"https://en.wikipedia.org/wiki/Law_of_triviality",[34],[2610],{"type":27,"value":2611},"bikeshedding",{"type":27,"value":2613}," takes place.",{"type":22,"tag":115,"props":2615,"children":2616},{},[2617,2622],{"type":22,"tag":2598,"props":2618,"children":2619},{},[2620],{"type":27,"value":2621},"Configuring tools:",{"type":27,"value":2623}," To save developers time, tooling can be used to catch or even to automatically fix violations of this style, but it takes a little time to configure that tooling.",{"type":22,"tag":115,"props":2625,"children":2626},{},[2627,2632],{"type":22,"tag":2598,"props":2628,"children":2629},{},[2630],{"type":27,"value":2631},"Thinking about style:",{"type":27,"value":2633}," Developers now need to be thinking about this style while they work. That might sound silly, but when a developer has their own differing preferences, or when they have experience using a different style (e.g. on previous teams or outside of work), each rule takes a little bit of brainpower to remember and to follow.",{"type":22,"tag":115,"props":2635,"children":2636},{},[2637,2642],{"type":22,"tag":2598,"props":2638,"children":2639},{},[2640],{"type":27,"value":2641},"Dealing with automated style rejections:",{"type":27,"value":2643}," Every time a tool rejects code due to style violations, a little time is wasted. This can for the most part be avoided using IDE integrations that auto-format code on save, but still affects certain areas (like using online code editing features in source control providers like GitHub or GitLab).",{"type":22,"tag":115,"props":2645,"children":2646},{},[2647,2652],{"type":22,"tag":2598,"props":2648,"children":2649},{},[2650],{"type":27,"value":2651},"Dealing with code review style rejections:",{"type":27,"value":2653}," Even worse than automated style rejections is when there is insufficient automation in place, and style errors are caught manually in code review.",{"type":22,"tag":23,"props":2655,"children":2656},{},[2657],{"type":27,"value":2658},"These costs may seem trivial, but given that most style guides have tens or hundreds of rules, they add up. Is it worth all of that just to have curly braces in the place the developers prefer? Or, to put it another way: does it matter where the curly braces are, if tooling can enforce it one way so seamlessly that developers never need to think about it?",{"type":22,"tag":193,"props":2660,"children":2662},{"id":2661},"the-benefits-of-having-no-style",[2663],{"type":27,"value":2664},"The Benefits of Having No Style",{"type":22,"tag":23,"props":2666,"children":2667},{},[2668],{"type":27,"value":2669},"Many mainstream languages today have one or more so-called \"opinionated code formatters\", for example:",{"type":22,"tag":111,"props":2671,"children":2672},{},[2673,2694,2706,2718,2734],{"type":22,"tag":115,"props":2674,"children":2675},{},[2676,2683,2685,2692],{"type":22,"tag":30,"props":2677,"children":2680},{"href":2678,"rel":2679},"https://prettier.io/",[34],[2681],{"type":27,"value":2682},"Prettier",{"type":27,"value":2684}," or ",{"type":22,"tag":30,"props":2686,"children":2689},{"href":2687,"rel":2688},"https://standardjs.com/",[34],[2690],{"type":27,"value":2691},"Standard JS",{"type":27,"value":2693}," for JavaScript and TypeScript",{"type":22,"tag":115,"props":2695,"children":2696},{},[2697,2704],{"type":22,"tag":30,"props":2698,"children":2701},{"href":2699,"rel":2700},"https://black.readthedocs.io/en/stable/",[34],[2702],{"type":27,"value":2703},"Black",{"type":27,"value":2705}," for Python",{"type":22,"tag":115,"props":2707,"children":2708},{},[2709,2716],{"type":22,"tag":30,"props":2710,"children":2713},{"href":2711,"rel":2712},"https://csharpier.com/",[34],[2714],{"type":27,"value":2715},"CSharpier",{"type":27,"value":2717}," for C#",{"type":22,"tag":115,"props":2719,"children":2720},{},[2721,2732],{"type":22,"tag":30,"props":2722,"children":2725},{"href":2723,"rel":2724},"https://github.com/rust-lang/rustfmt",[34],[2726],{"type":22,"tag":486,"props":2727,"children":2729},{"className":2728},[],[2730],{"type":27,"value":2731},"rustfmt",{"type":27,"value":2733}," for Rust",{"type":22,"tag":115,"props":2735,"children":2736},{},[2737,2748],{"type":22,"tag":30,"props":2738,"children":2741},{"href":2739,"rel":2740},"https://pkg.go.dev/cmd/gofmt",[34],[2742],{"type":22,"tag":486,"props":2743,"children":2745},{"className":2744},[],[2746],{"type":27,"value":2747},"gofmt",{"type":27,"value":2749}," for Go",{"type":22,"tag":23,"props":2751,"children":2752},{},[2753],{"type":27,"value":2754},"They're called \"opinionated\" because they provide little to no configuration capabilities, instead deciding most things for you. If your language of choice is in the above, I encourage you to look at an example of one of those formatters' outputs. What do you think? It likely follows several rules you would choose differently, and you could likely produce nicer code than it can by judiciously breaking the rules in special cases where it's appropriate to do so. But I strongly suspect that overall, you'll find that the output looks... fine. It's readable. It works.",{"type":22,"tag":23,"props":2756,"children":2757},{},[2758,2760,2766],{"type":27,"value":2759},"Now, consider that with a just a few lines of configuration, you could have your editor automatically format your code on save to fully match the style, with additional checks on ",{"type":22,"tag":486,"props":2761,"children":2763},{"className":2762},[],[2764],{"type":27,"value":2765},"git push",{"type":27,"value":2767}," and/or in CI in case anything slipped through.",{"type":22,"tag":23,"props":2769,"children":2770},{},[2771],{"type":27,"value":2772},"No time spent with a committee of developers deciding on the best place for curly braces.",{"type":22,"tag":23,"props":2774,"children":2775},{},[2776],{"type":27,"value":2777},"No time spent mentally context-switching between a work codebase that does things one way and an open-source codebase that does things another way.",{"type":22,"tag":23,"props":2779,"children":2780},{},[2781],{"type":27,"value":2782},"No time spent in code reviews pointing out minor style violations.",{"type":22,"tag":23,"props":2784,"children":2785},{},[2786,2788,2793],{"type":27,"value":2787},"Just type out a hideous-yet-functional stream of code in whatever way suits you, hit \"save\", watch your code instantly get prettified for you, and move on. That doesn't sound like much, but once you've experienced it, it's hard to go back. And once you get used to tolerating some rules you disagree with, you start to realize that what's more important than seeing your preferred style is seeing a ",{"type":22,"tag":267,"props":2789,"children":2790},{},[2791],{"type":27,"value":2792},"consistent",{"type":27,"value":2794}," style, and that's something that can be fully automated.",{"type":22,"tag":23,"props":2796,"children":2797},{},[2798],{"type":27,"value":2799},"The next time you're configuring a project, consider using an opinionated code formatter, and saving your brain power for solving the real problems.",{"type":22,"tag":1999,"props":2801,"children":2802},{},[2803],{"type":27,"value":2457},{"title":8,"searchDepth":755,"depth":755,"links":2805},[2806,2807],{"id":2494,"depth":758,"text":2497},{"id":2661,"depth":758,"text":2664},"content:phendry:2023-01-31:ForgetAboutCodeStyle.md","phendry/2023-01-31/ForgetAboutCodeStyle.md","phendry/2023-01-31/ForgetAboutCodeStyle",{"user":774,"name":775},{"_path":2813,"_dir":2814,"_draft":7,"_partial":7,"_locale":8,"title":2815,"description":2816,"tags":2817,"image":2820,"publishDate":2821,"excerpt":2816,"body":2822,"_type":767,"_id":4294,"_source":769,"_file":4295,"_stem":4296,"_extension":772,"author":4297},"/phendry/2023-01-19/badcode","2023-01-19","\"Bad\" Code (Or, Why Software Development is Hard)","Recently, the Dutch government open-sourced the iOS application for their \"DigiD\" authentication service. A tweet with a snippet of that source code, presumably making fun of it, blew up into a debate about whether mocking it is even justified. The amount of debate over such a simple snippet of code highlights, in my mind, just how tricky software development can be.",[2818,2819],"security","series","/phendry/2023-01-19/img/Bad Code.png","2024-01-15",{"type":19,"children":2823,"toc":4289},[2824,2847,2852,3511,3539,3572,3757,3762,3767,3781,3786,3791,3796,3802,3807,3859,3878,3890,3895,3901,3913,3926,3931,4224,4253,4259,4264,4285],{"type":22,"tag":23,"props":2825,"children":2826},{},[2827,2829,2836,2838,2845],{"type":27,"value":2828},"Recently, the Dutch government open-sourced the iOS application for their \"DigiD\" authentication service. A ",{"type":22,"tag":30,"props":2830,"children":2833},{"href":2831,"rel":2832},"https://twitter.com/JeroenFrijters/status/1615204074588180481",[34],[2834],{"type":27,"value":2835},"tweet",{"type":27,"value":2837}," with a snippet of that source code, presumably making fun of it, blew up into ",{"type":22,"tag":30,"props":2839,"children":2842},{"href":2840,"rel":2841},"https://old.reddit.com/r/ProgrammerHumor/comments/10dh6x1/very_efficient_code/",[34],[2843],{"type":27,"value":2844},"a debate",{"type":27,"value":2846}," about whether mocking it is even justified. The amount of debate over such a simple snippet of code highlights, in my mind, just how tricky software development can be.",{"type":22,"tag":23,"props":2848,"children":2849},{},[2850],{"type":27,"value":2851},"Here is the code in question, written in C#:",{"type":22,"tag":886,"props":2853,"children":2857},{"className":2854,"code":2855,"language":2856,"meta":8,"style":8},"language-csharp shiki shiki-themes github-light github-dark","private static string GetPercentageRounds(double percentage)\n{\n    if (percentage == 0)\n        return \"⚪⚪⚪⚪⚪⚪⚪⚪⚪⚪\";\n    if (percentage > 0.0 && percentage \u003C= 0.1)\n        return \"🔵⚪⚪⚪⚪⚪⚪⚪⚪⚪\";\n    if (percentage > 0.1 && percentage \u003C= 0.2)\n        return \"🔵🔵⚪⚪⚪⚪⚪⚪⚪⚪\";\n    if (percentage > 0.2 && percentage \u003C= 0.3)\n        return \"🔵🔵🔵⚪⚪⚪⚪⚪⚪⚪\";\n    if (percentage > 0.3 && percentage \u003C= 0.4)\n        return \"🔵🔵🔵🔵⚪⚪⚪⚪⚪⚪\";\n    if (percentage > 0.4 && percentage \u003C= 0.5)\n        return \"🔵🔵🔵🔵🔵⚪⚪⚪⚪⚪\";\n    if (percentage > 0.5 && percentage \u003C= 0.6)\n        return \"🔵🔵🔵🔵🔵🔵⚪⚪⚪⚪\";\n    if (percentage > 0.6 && percentage \u003C= 0.7)\n        return \"🔵🔵🔵🔵🔵🔵🔵⚪⚪⚪\";\n    if (percentage > 0.7 && percentage \u003C= 0.8)\n        return \"🔵🔵🔵🔵🔵🔵🔵🔵⚪⚪\";\n    if (percentage > 0.8 && percentage \u003C= 0.9)\n        return \"🔵🔵🔵🔵🔵🔵🔵🔵🔵⚪\";\n\n    return \"🔵🔵🔵🔵🔵🔵🔵🔵🔵🔵\";\n}\n","csharp",[2858],{"type":22,"tag":486,"props":2859,"children":2860},{"__ignoreMap":8},[2861,2904,2911,2938,2955,3000,3016,3056,3072,3112,3128,3168,3185,3226,3243,3284,3301,3342,3359,3400,3417,3458,3475,3485,3503],{"type":22,"tag":896,"props":2862,"children":2863},{"class":898,"line":899},[2864,2869,2874,2879,2884,2889,2894,2899],{"type":22,"tag":896,"props":2865,"children":2866},{"style":1691},[2867],{"type":27,"value":2868},"private",{"type":22,"tag":896,"props":2870,"children":2871},{"style":1691},[2872],{"type":27,"value":2873}," static",{"type":22,"tag":896,"props":2875,"children":2876},{"style":1691},[2877],{"type":27,"value":2878}," string",{"type":22,"tag":896,"props":2880,"children":2881},{"style":933},[2882],{"type":27,"value":2883}," GetPercentageRounds",{"type":22,"tag":896,"props":2885,"children":2886},{"style":903},[2887],{"type":27,"value":2888},"(",{"type":22,"tag":896,"props":2890,"children":2891},{"style":1691},[2892],{"type":27,"value":2893},"double",{"type":22,"tag":896,"props":2895,"children":2896},{"style":933},[2897],{"type":27,"value":2898}," percentage",{"type":22,"tag":896,"props":2900,"children":2901},{"style":903},[2902],{"type":27,"value":2903},")\n",{"type":22,"tag":896,"props":2905,"children":2906},{"class":898,"line":758},[2907],{"type":22,"tag":896,"props":2908,"children":2909},{"style":903},[2910],{"type":27,"value":2571},{"type":22,"tag":896,"props":2912,"children":2913},{"class":898,"line":755},[2914,2919,2924,2929,2934],{"type":22,"tag":896,"props":2915,"children":2916},{"style":1691},[2917],{"type":27,"value":2918},"    if",{"type":22,"tag":896,"props":2920,"children":2921},{"style":903},[2922],{"type":27,"value":2923}," (percentage ",{"type":22,"tag":896,"props":2925,"children":2926},{"style":1691},[2927],{"type":27,"value":2928},"==",{"type":22,"tag":896,"props":2930,"children":2931},{"style":1632},[2932],{"type":27,"value":2933}," 0",{"type":22,"tag":896,"props":2935,"children":2936},{"style":903},[2937],{"type":27,"value":2903},{"type":22,"tag":896,"props":2939,"children":2940},{"class":898,"line":983},[2941,2946,2951],{"type":22,"tag":896,"props":2942,"children":2943},{"style":1691},[2944],{"type":27,"value":2945},"        return",{"type":22,"tag":896,"props":2947,"children":2948},{"style":944},[2949],{"type":27,"value":2950}," \"⚪⚪⚪⚪⚪⚪⚪⚪⚪⚪\"",{"type":22,"tag":896,"props":2952,"children":2953},{"style":903},[2954],{"type":27,"value":1649},{"type":22,"tag":896,"props":2956,"children":2957},{"class":898,"line":1013},[2958,2962,2966,2971,2976,2981,2986,2991,2996],{"type":22,"tag":896,"props":2959,"children":2960},{"style":1691},[2961],{"type":27,"value":2918},{"type":22,"tag":896,"props":2963,"children":2964},{"style":903},[2965],{"type":27,"value":2923},{"type":22,"tag":896,"props":2967,"children":2968},{"style":1691},[2969],{"type":27,"value":2970},">",{"type":22,"tag":896,"props":2972,"children":2973},{"style":1632},[2974],{"type":27,"value":2975}," 0.0",{"type":22,"tag":896,"props":2977,"children":2978},{"style":1691},[2979],{"type":27,"value":2980}," &&",{"type":22,"tag":896,"props":2982,"children":2983},{"style":903},[2984],{"type":27,"value":2985}," percentage ",{"type":22,"tag":896,"props":2987,"children":2988},{"style":1691},[2989],{"type":27,"value":2990},"\u003C=",{"type":22,"tag":896,"props":2992,"children":2993},{"style":1632},[2994],{"type":27,"value":2995}," 0.1",{"type":22,"tag":896,"props":2997,"children":2998},{"style":903},[2999],{"type":27,"value":2903},{"type":22,"tag":896,"props":3001,"children":3002},{"class":898,"line":1030},[3003,3007,3012],{"type":22,"tag":896,"props":3004,"children":3005},{"style":1691},[3006],{"type":27,"value":2945},{"type":22,"tag":896,"props":3008,"children":3009},{"style":944},[3010],{"type":27,"value":3011}," \"🔵⚪⚪⚪⚪⚪⚪⚪⚪⚪\"",{"type":22,"tag":896,"props":3013,"children":3014},{"style":903},[3015],{"type":27,"value":1649},{"type":22,"tag":896,"props":3017,"children":3018},{"class":898,"line":1068},[3019,3023,3027,3031,3035,3039,3043,3047,3052],{"type":22,"tag":896,"props":3020,"children":3021},{"style":1691},[3022],{"type":27,"value":2918},{"type":22,"tag":896,"props":3024,"children":3025},{"style":903},[3026],{"type":27,"value":2923},{"type":22,"tag":896,"props":3028,"children":3029},{"style":1691},[3030],{"type":27,"value":2970},{"type":22,"tag":896,"props":3032,"children":3033},{"style":1632},[3034],{"type":27,"value":2995},{"type":22,"tag":896,"props":3036,"children":3037},{"style":1691},[3038],{"type":27,"value":2980},{"type":22,"tag":896,"props":3040,"children":3041},{"style":903},[3042],{"type":27,"value":2985},{"type":22,"tag":896,"props":3044,"children":3045},{"style":1691},[3046],{"type":27,"value":2990},{"type":22,"tag":896,"props":3048,"children":3049},{"style":1632},[3050],{"type":27,"value":3051}," 0.2",{"type":22,"tag":896,"props":3053,"children":3054},{"style":903},[3055],{"type":27,"value":2903},{"type":22,"tag":896,"props":3057,"children":3058},{"class":898,"line":1085},[3059,3063,3068],{"type":22,"tag":896,"props":3060,"children":3061},{"style":1691},[3062],{"type":27,"value":2945},{"type":22,"tag":896,"props":3064,"children":3065},{"style":944},[3066],{"type":27,"value":3067}," \"🔵🔵⚪⚪⚪⚪⚪⚪⚪⚪\"",{"type":22,"tag":896,"props":3069,"children":3070},{"style":903},[3071],{"type":27,"value":1649},{"type":22,"tag":896,"props":3073,"children":3074},{"class":898,"line":1123},[3075,3079,3083,3087,3091,3095,3099,3103,3108],{"type":22,"tag":896,"props":3076,"children":3077},{"style":1691},[3078],{"type":27,"value":2918},{"type":22,"tag":896,"props":3080,"children":3081},{"style":903},[3082],{"type":27,"value":2923},{"type":22,"tag":896,"props":3084,"children":3085},{"style":1691},[3086],{"type":27,"value":2970},{"type":22,"tag":896,"props":3088,"children":3089},{"style":1632},[3090],{"type":27,"value":3051},{"type":22,"tag":896,"props":3092,"children":3093},{"style":1691},[3094],{"type":27,"value":2980},{"type":22,"tag":896,"props":3096,"children":3097},{"style":903},[3098],{"type":27,"value":2985},{"type":22,"tag":896,"props":3100,"children":3101},{"style":1691},[3102],{"type":27,"value":2990},{"type":22,"tag":896,"props":3104,"children":3105},{"style":1632},[3106],{"type":27,"value":3107}," 0.3",{"type":22,"tag":896,"props":3109,"children":3110},{"style":903},[3111],{"type":27,"value":2903},{"type":22,"tag":896,"props":3113,"children":3114},{"class":898,"line":1161},[3115,3119,3124],{"type":22,"tag":896,"props":3116,"children":3117},{"style":1691},[3118],{"type":27,"value":2945},{"type":22,"tag":896,"props":3120,"children":3121},{"style":944},[3122],{"type":27,"value":3123}," \"🔵🔵🔵⚪⚪⚪⚪⚪⚪⚪\"",{"type":22,"tag":896,"props":3125,"children":3126},{"style":903},[3127],{"type":27,"value":1649},{"type":22,"tag":896,"props":3129,"children":3130},{"class":898,"line":2298},[3131,3135,3139,3143,3147,3151,3155,3159,3164],{"type":22,"tag":896,"props":3132,"children":3133},{"style":1691},[3134],{"type":27,"value":2918},{"type":22,"tag":896,"props":3136,"children":3137},{"style":903},[3138],{"type":27,"value":2923},{"type":22,"tag":896,"props":3140,"children":3141},{"style":1691},[3142],{"type":27,"value":2970},{"type":22,"tag":896,"props":3144,"children":3145},{"style":1632},[3146],{"type":27,"value":3107},{"type":22,"tag":896,"props":3148,"children":3149},{"style":1691},[3150],{"type":27,"value":2980},{"type":22,"tag":896,"props":3152,"children":3153},{"style":903},[3154],{"type":27,"value":2985},{"type":22,"tag":896,"props":3156,"children":3157},{"style":1691},[3158],{"type":27,"value":2990},{"type":22,"tag":896,"props":3160,"children":3161},{"style":1632},[3162],{"type":27,"value":3163}," 0.4",{"type":22,"tag":896,"props":3165,"children":3166},{"style":903},[3167],{"type":27,"value":2903},{"type":22,"tag":896,"props":3169,"children":3171},{"class":898,"line":3170},12,[3172,3176,3181],{"type":22,"tag":896,"props":3173,"children":3174},{"style":1691},[3175],{"type":27,"value":2945},{"type":22,"tag":896,"props":3177,"children":3178},{"style":944},[3179],{"type":27,"value":3180}," \"🔵🔵🔵🔵⚪⚪⚪⚪⚪⚪\"",{"type":22,"tag":896,"props":3182,"children":3183},{"style":903},[3184],{"type":27,"value":1649},{"type":22,"tag":896,"props":3186,"children":3188},{"class":898,"line":3187},13,[3189,3193,3197,3201,3205,3209,3213,3217,3222],{"type":22,"tag":896,"props":3190,"children":3191},{"style":1691},[3192],{"type":27,"value":2918},{"type":22,"tag":896,"props":3194,"children":3195},{"style":903},[3196],{"type":27,"value":2923},{"type":22,"tag":896,"props":3198,"children":3199},{"style":1691},[3200],{"type":27,"value":2970},{"type":22,"tag":896,"props":3202,"children":3203},{"style":1632},[3204],{"type":27,"value":3163},{"type":22,"tag":896,"props":3206,"children":3207},{"style":1691},[3208],{"type":27,"value":2980},{"type":22,"tag":896,"props":3210,"children":3211},{"style":903},[3212],{"type":27,"value":2985},{"type":22,"tag":896,"props":3214,"children":3215},{"style":1691},[3216],{"type":27,"value":2990},{"type":22,"tag":896,"props":3218,"children":3219},{"style":1632},[3220],{"type":27,"value":3221}," 0.5",{"type":22,"tag":896,"props":3223,"children":3224},{"style":903},[3225],{"type":27,"value":2903},{"type":22,"tag":896,"props":3227,"children":3229},{"class":898,"line":3228},14,[3230,3234,3239],{"type":22,"tag":896,"props":3231,"children":3232},{"style":1691},[3233],{"type":27,"value":2945},{"type":22,"tag":896,"props":3235,"children":3236},{"style":944},[3237],{"type":27,"value":3238}," \"🔵🔵🔵🔵🔵⚪⚪⚪⚪⚪\"",{"type":22,"tag":896,"props":3240,"children":3241},{"style":903},[3242],{"type":27,"value":1649},{"type":22,"tag":896,"props":3244,"children":3246},{"class":898,"line":3245},15,[3247,3251,3255,3259,3263,3267,3271,3275,3280],{"type":22,"tag":896,"props":3248,"children":3249},{"style":1691},[3250],{"type":27,"value":2918},{"type":22,"tag":896,"props":3252,"children":3253},{"style":903},[3254],{"type":27,"value":2923},{"type":22,"tag":896,"props":3256,"children":3257},{"style":1691},[3258],{"type":27,"value":2970},{"type":22,"tag":896,"props":3260,"children":3261},{"style":1632},[3262],{"type":27,"value":3221},{"type":22,"tag":896,"props":3264,"children":3265},{"style":1691},[3266],{"type":27,"value":2980},{"type":22,"tag":896,"props":3268,"children":3269},{"style":903},[3270],{"type":27,"value":2985},{"type":22,"tag":896,"props":3272,"children":3273},{"style":1691},[3274],{"type":27,"value":2990},{"type":22,"tag":896,"props":3276,"children":3277},{"style":1632},[3278],{"type":27,"value":3279}," 0.6",{"type":22,"tag":896,"props":3281,"children":3282},{"style":903},[3283],{"type":27,"value":2903},{"type":22,"tag":896,"props":3285,"children":3287},{"class":898,"line":3286},16,[3288,3292,3297],{"type":22,"tag":896,"props":3289,"children":3290},{"style":1691},[3291],{"type":27,"value":2945},{"type":22,"tag":896,"props":3293,"children":3294},{"style":944},[3295],{"type":27,"value":3296}," \"🔵🔵🔵🔵🔵🔵⚪⚪⚪⚪\"",{"type":22,"tag":896,"props":3298,"children":3299},{"style":903},[3300],{"type":27,"value":1649},{"type":22,"tag":896,"props":3302,"children":3304},{"class":898,"line":3303},17,[3305,3309,3313,3317,3321,3325,3329,3333,3338],{"type":22,"tag":896,"props":3306,"children":3307},{"style":1691},[3308],{"type":27,"value":2918},{"type":22,"tag":896,"props":3310,"children":3311},{"style":903},[3312],{"type":27,"value":2923},{"type":22,"tag":896,"props":3314,"children":3315},{"style":1691},[3316],{"type":27,"value":2970},{"type":22,"tag":896,"props":3318,"children":3319},{"style":1632},[3320],{"type":27,"value":3279},{"type":22,"tag":896,"props":3322,"children":3323},{"style":1691},[3324],{"type":27,"value":2980},{"type":22,"tag":896,"props":3326,"children":3327},{"style":903},[3328],{"type":27,"value":2985},{"type":22,"tag":896,"props":3330,"children":3331},{"style":1691},[3332],{"type":27,"value":2990},{"type":22,"tag":896,"props":3334,"children":3335},{"style":1632},[3336],{"type":27,"value":3337}," 0.7",{"type":22,"tag":896,"props":3339,"children":3340},{"style":903},[3341],{"type":27,"value":2903},{"type":22,"tag":896,"props":3343,"children":3345},{"class":898,"line":3344},18,[3346,3350,3355],{"type":22,"tag":896,"props":3347,"children":3348},{"style":1691},[3349],{"type":27,"value":2945},{"type":22,"tag":896,"props":3351,"children":3352},{"style":944},[3353],{"type":27,"value":3354}," \"🔵🔵🔵🔵🔵🔵🔵⚪⚪⚪\"",{"type":22,"tag":896,"props":3356,"children":3357},{"style":903},[3358],{"type":27,"value":1649},{"type":22,"tag":896,"props":3360,"children":3362},{"class":898,"line":3361},19,[3363,3367,3371,3375,3379,3383,3387,3391,3396],{"type":22,"tag":896,"props":3364,"children":3365},{"style":1691},[3366],{"type":27,"value":2918},{"type":22,"tag":896,"props":3368,"children":3369},{"style":903},[3370],{"type":27,"value":2923},{"type":22,"tag":896,"props":3372,"children":3373},{"style":1691},[3374],{"type":27,"value":2970},{"type":22,"tag":896,"props":3376,"children":3377},{"style":1632},[3378],{"type":27,"value":3337},{"type":22,"tag":896,"props":3380,"children":3381},{"style":1691},[3382],{"type":27,"value":2980},{"type":22,"tag":896,"props":3384,"children":3385},{"style":903},[3386],{"type":27,"value":2985},{"type":22,"tag":896,"props":3388,"children":3389},{"style":1691},[3390],{"type":27,"value":2990},{"type":22,"tag":896,"props":3392,"children":3393},{"style":1632},[3394],{"type":27,"value":3395}," 0.8",{"type":22,"tag":896,"props":3397,"children":3398},{"style":903},[3399],{"type":27,"value":2903},{"type":22,"tag":896,"props":3401,"children":3403},{"class":898,"line":3402},20,[3404,3408,3413],{"type":22,"tag":896,"props":3405,"children":3406},{"style":1691},[3407],{"type":27,"value":2945},{"type":22,"tag":896,"props":3409,"children":3410},{"style":944},[3411],{"type":27,"value":3412}," \"🔵🔵🔵🔵🔵🔵🔵🔵⚪⚪\"",{"type":22,"tag":896,"props":3414,"children":3415},{"style":903},[3416],{"type":27,"value":1649},{"type":22,"tag":896,"props":3418,"children":3420},{"class":898,"line":3419},21,[3421,3425,3429,3433,3437,3441,3445,3449,3454],{"type":22,"tag":896,"props":3422,"children":3423},{"style":1691},[3424],{"type":27,"value":2918},{"type":22,"tag":896,"props":3426,"children":3427},{"style":903},[3428],{"type":27,"value":2923},{"type":22,"tag":896,"props":3430,"children":3431},{"style":1691},[3432],{"type":27,"value":2970},{"type":22,"tag":896,"props":3434,"children":3435},{"style":1632},[3436],{"type":27,"value":3395},{"type":22,"tag":896,"props":3438,"children":3439},{"style":1691},[3440],{"type":27,"value":2980},{"type":22,"tag":896,"props":3442,"children":3443},{"style":903},[3444],{"type":27,"value":2985},{"type":22,"tag":896,"props":3446,"children":3447},{"style":1691},[3448],{"type":27,"value":2990},{"type":22,"tag":896,"props":3450,"children":3451},{"style":1632},[3452],{"type":27,"value":3453}," 0.9",{"type":22,"tag":896,"props":3455,"children":3456},{"style":903},[3457],{"type":27,"value":2903},{"type":22,"tag":896,"props":3459,"children":3461},{"class":898,"line":3460},22,[3462,3466,3471],{"type":22,"tag":896,"props":3463,"children":3464},{"style":1691},[3465],{"type":27,"value":2945},{"type":22,"tag":896,"props":3467,"children":3468},{"style":944},[3469],{"type":27,"value":3470}," \"🔵🔵🔵🔵🔵🔵🔵🔵🔵⚪\"",{"type":22,"tag":896,"props":3472,"children":3473},{"style":903},[3474],{"type":27,"value":1649},{"type":22,"tag":896,"props":3476,"children":3478},{"class":898,"line":3477},23,[3479],{"type":22,"tag":896,"props":3480,"children":3482},{"emptyLinePlaceholder":3481},true,[3483],{"type":27,"value":3484},"\n",{"type":22,"tag":896,"props":3486,"children":3488},{"class":898,"line":3487},24,[3489,3494,3499],{"type":22,"tag":896,"props":3490,"children":3491},{"style":1691},[3492],{"type":27,"value":3493},"    return",{"type":22,"tag":896,"props":3495,"children":3496},{"style":944},[3497],{"type":27,"value":3498}," \"🔵🔵🔵🔵🔵🔵🔵🔵🔵🔵\"",{"type":22,"tag":896,"props":3500,"children":3501},{"style":903},[3502],{"type":27,"value":1649},{"type":22,"tag":896,"props":3504,"children":3506},{"class":898,"line":3505},25,[3507],{"type":22,"tag":896,"props":3508,"children":3509},{"style":903},[3510],{"type":27,"value":1745},{"type":22,"tag":23,"props":3512,"children":3513},{},[3514,3516,3522,3524,3530,3532,3537],{"type":27,"value":3515},"Let's ignore the lack of parameter validation (namely of ensuring that ",{"type":22,"tag":486,"props":3517,"children":3519},{"className":3518},[],[3520],{"type":27,"value":3521},"percentage",{"type":27,"value":3523}," is not ",{"type":22,"tag":486,"props":3525,"children":3527},{"className":3526},[],[3528],{"type":27,"value":3529},"NaN",{"type":27,"value":3531},", negative, or infinity), since it would be the same regardless of solution, and it's not uncommon to omit that sort of rigorous error handling in a ",{"type":22,"tag":486,"props":3533,"children":3535},{"className":3534},[],[3536],{"type":27,"value":2868},{"type":27,"value":3538}," method.",{"type":22,"tag":23,"props":3540,"children":3541},{},[3542,3544,3549,3551,3556,3558,3563,3565,3570],{"type":27,"value":3543},"Aside from that, well... it omits curly brackets in the ",{"type":22,"tag":486,"props":3545,"children":3547},{"className":3546},[],[3548],{"type":27,"value":2519},{"type":27,"value":3550}," blocks, which is a style thing many people dislike. It's ",{"type":22,"tag":267,"props":3552,"children":3553},{},[3554],{"type":27,"value":3555},"very",{"type":27,"value":3557}," repetitious. It has unnecessary greater-than conditions (since each prior ",{"type":22,"tag":486,"props":3559,"children":3561},{"className":3560},[],[3562],{"type":27,"value":2519},{"type":27,"value":3564},"'s less-than-equal condition guarantees it). It does several more ",{"type":22,"tag":486,"props":3566,"children":3568},{"className":3567},[],[3569],{"type":27,"value":2519},{"type":27,"value":3571}," comparisons than it technically needs to. In fact, given that the output is linearly related to the input, this can be a one-liner with just a little bit of math; let's call it the \"math solution\".",{"type":22,"tag":886,"props":3573,"children":3575},{"className":2854,"code":3574,"language":2856,"meta":8,"style":8},"private static string GetPercentageRounds(double percentage)\n{\n    int stage = (int)Math.Ceiling(percentage * 10);\n    return new string('🔵', stage) + new string('⚪', 10 - stage);\n}\n",[3576],{"type":22,"tag":486,"props":3577,"children":3578},{"__ignoreMap":8},[3579,3614,3621,3679,3750],{"type":22,"tag":896,"props":3580,"children":3581},{"class":898,"line":899},[3582,3586,3590,3594,3598,3602,3606,3610],{"type":22,"tag":896,"props":3583,"children":3584},{"style":1691},[3585],{"type":27,"value":2868},{"type":22,"tag":896,"props":3587,"children":3588},{"style":1691},[3589],{"type":27,"value":2873},{"type":22,"tag":896,"props":3591,"children":3592},{"style":1691},[3593],{"type":27,"value":2878},{"type":22,"tag":896,"props":3595,"children":3596},{"style":933},[3597],{"type":27,"value":2883},{"type":22,"tag":896,"props":3599,"children":3600},{"style":903},[3601],{"type":27,"value":2888},{"type":22,"tag":896,"props":3603,"children":3604},{"style":1691},[3605],{"type":27,"value":2893},{"type":22,"tag":896,"props":3607,"children":3608},{"style":933},[3609],{"type":27,"value":2898},{"type":22,"tag":896,"props":3611,"children":3612},{"style":903},[3613],{"type":27,"value":2903},{"type":22,"tag":896,"props":3615,"children":3616},{"class":898,"line":758},[3617],{"type":22,"tag":896,"props":3618,"children":3619},{"style":903},[3620],{"type":27,"value":2571},{"type":22,"tag":896,"props":3622,"children":3623},{"class":898,"line":755},[3624,3629,3634,3639,3644,3649,3654,3659,3664,3669,3674],{"type":22,"tag":896,"props":3625,"children":3626},{"style":1691},[3627],{"type":27,"value":3628},"    int",{"type":22,"tag":896,"props":3630,"children":3631},{"style":933},[3632],{"type":27,"value":3633}," stage",{"type":22,"tag":896,"props":3635,"children":3636},{"style":1691},[3637],{"type":27,"value":3638}," =",{"type":22,"tag":896,"props":3640,"children":3641},{"style":903},[3642],{"type":27,"value":3643}," (",{"type":22,"tag":896,"props":3645,"children":3646},{"style":1691},[3647],{"type":27,"value":3648},"int",{"type":22,"tag":896,"props":3650,"children":3651},{"style":903},[3652],{"type":27,"value":3653},")Math.",{"type":22,"tag":896,"props":3655,"children":3656},{"style":933},[3657],{"type":27,"value":3658},"Ceiling",{"type":22,"tag":896,"props":3660,"children":3661},{"style":903},[3662],{"type":27,"value":3663},"(percentage ",{"type":22,"tag":896,"props":3665,"children":3666},{"style":1691},[3667],{"type":27,"value":3668},"*",{"type":22,"tag":896,"props":3670,"children":3671},{"style":1632},[3672],{"type":27,"value":3673}," 10",{"type":22,"tag":896,"props":3675,"children":3676},{"style":903},[3677],{"type":27,"value":3678},");\n",{"type":22,"tag":896,"props":3680,"children":3681},{"class":898,"line":983},[3682,3686,3691,3695,3699,3704,3709,3714,3718,3722,3726,3731,3735,3740,3745],{"type":22,"tag":896,"props":3683,"children":3684},{"style":1691},[3685],{"type":27,"value":3493},{"type":22,"tag":896,"props":3687,"children":3688},{"style":1691},[3689],{"type":27,"value":3690}," new",{"type":22,"tag":896,"props":3692,"children":3693},{"style":1691},[3694],{"type":27,"value":2878},{"type":22,"tag":896,"props":3696,"children":3697},{"style":903},[3698],{"type":27,"value":2888},{"type":22,"tag":896,"props":3700,"children":3701},{"style":944},[3702],{"type":27,"value":3703},"'🔵'",{"type":22,"tag":896,"props":3705,"children":3706},{"style":903},[3707],{"type":27,"value":3708},", stage) ",{"type":22,"tag":896,"props":3710,"children":3711},{"style":1691},[3712],{"type":27,"value":3713},"+",{"type":22,"tag":896,"props":3715,"children":3716},{"style":1691},[3717],{"type":27,"value":3690},{"type":22,"tag":896,"props":3719,"children":3720},{"style":1691},[3721],{"type":27,"value":2878},{"type":22,"tag":896,"props":3723,"children":3724},{"style":903},[3725],{"type":27,"value":2888},{"type":22,"tag":896,"props":3727,"children":3728},{"style":944},[3729],{"type":27,"value":3730},"'⚪'",{"type":22,"tag":896,"props":3732,"children":3733},{"style":903},[3734],{"type":27,"value":236},{"type":22,"tag":896,"props":3736,"children":3737},{"style":1632},[3738],{"type":27,"value":3739},"10",{"type":22,"tag":896,"props":3741,"children":3742},{"style":1691},[3743],{"type":27,"value":3744}," -",{"type":22,"tag":896,"props":3746,"children":3747},{"style":903},[3748],{"type":27,"value":3749}," stage);\n",{"type":22,"tag":896,"props":3751,"children":3752},{"class":898,"line":1013},[3753],{"type":22,"tag":896,"props":3754,"children":3755},{"style":903},[3756],{"type":27,"value":1745},{"type":22,"tag":23,"props":3758,"children":3759},{},[3760],{"type":27,"value":3761},"Repetition avoided; 21 lines reduced to 2. Problem solved...",{"type":22,"tag":23,"props":3763,"children":3764},{},[3765],{"type":27,"value":3766},"...although if I were come back to that code in a month's time, I'd definitely have to stare at it longer to figure out what it does...",{"type":22,"tag":23,"props":3768,"children":3769},{},[3770,3772,3779],{"type":27,"value":3771},"...and I'm not 100% confident that the multiplication won't cause ",{"type":22,"tag":30,"props":3773,"children":3776},{"href":3774,"rel":3775},"https://floating-point-gui.de/errors/comparison/",[34],[3777],{"type":27,"value":3778},"floating point edge cases",{"type":27,"value":3780},"...",{"type":22,"tag":23,"props":3782,"children":3783},{},[3784],{"type":27,"value":3785},"...and I guess it's less flexible now (e.g. to adjust the color of individual dots by stage)...",{"type":22,"tag":23,"props":3787,"children":3788},{},[3789],{"type":27,"value":3790},"...and it's doing three string allocations now instead of returning a constant, so maybe that has performance implications...",{"type":22,"tag":23,"props":3792,"children":3793},{},[3794],{"type":27,"value":3795},"...hmm. Maybe we need to remind ourselves of what \"good\" code is.",{"type":22,"tag":193,"props":3797,"children":3799},{"id":3798},"question-what-is-good-code-answer-code-that-makes-the-right-tradeoffs",[3800],{"type":27,"value":3801},"Question: What is Good Code? Answer: Code That Makes The Right Tradeoffs.",{"type":22,"tag":23,"props":3803,"children":3804},{},[3805],{"type":27,"value":3806},"Good code has a number of qualities most developers would agree on:",{"type":22,"tag":847,"props":3808,"children":3809},{},[3810,3822,3839,3849],{"type":22,"tag":115,"props":3811,"children":3812},{},[3813,3815,3820],{"type":27,"value":3814},"Arguably the most important, ",{"type":22,"tag":2598,"props":3816,"children":3817},{},[3818],{"type":27,"value":3819},"good code is readable",{"type":27,"value":3821},". Code is written once but it is read many times, and understanding code is key to maintaining and extending it, so a solution that is easy to read and understand is usually better than one that is more clever.",{"type":22,"tag":115,"props":3823,"children":3824},{},[3825,3830,3832,3837],{"type":22,"tag":2598,"props":3826,"children":3827},{},[3828],{"type":27,"value":3829},"Good code is correct",{"type":27,"value":3831},"; it does what it's supposed to do. OK, ",{"type":22,"tag":267,"props":3833,"children":3834},{},[3835],{"type":27,"value":3836},"maybe",{"type":27,"value":3838}," this is more important than readability, but then again, it's easy to fix a bug in readable code.",{"type":22,"tag":115,"props":3840,"children":3841},{},[3842,3847],{"type":22,"tag":2598,"props":3843,"children":3844},{},[3845],{"type":27,"value":3846},"Good code is performant",{"type":27,"value":3848},"; it should not needlessly waste system resources.",{"type":22,"tag":115,"props":3850,"children":3851},{},[3852,3857],{"type":22,"tag":2598,"props":3853,"children":3854},{},[3855],{"type":27,"value":3856},"Good code is easy to write",{"type":27,"value":3858},", since productivity is important.",{"type":22,"tag":23,"props":3860,"children":3861},{},[3862,3864,3869,3871,3876],{"type":27,"value":3863},"Often, even ",{"type":22,"tag":267,"props":3865,"children":3866},{},[3867],{"type":27,"value":3868},"usually",{"type":27,"value":3870},", some of these qualities are at odds with one another. Making code more readable often means not choosing the most performant possible solution, and similarly, optimizing a solution often makes it less readable. Code that's easy to write is often not readable, performant ",{"type":22,"tag":267,"props":3872,"children":3873},{},[3874],{"type":27,"value":3875},"or",{"type":27,"value":3877}," correct.",{"type":22,"tag":23,"props":3879,"children":3880},{},[3881,3883,3888],{"type":27,"value":3882},"What this means is that what makes for \"good\" code always depends on the circumstances of that code: its purpose, the code surrounding it, the current and future priorities of the end client or product manager, etc. We don't even ",{"type":22,"tag":267,"props":3884,"children":3885},{},[3886],{"type":27,"value":3887},"know",{"type":27,"value":3889}," all those circumstances at the time the code is written, so we're doomed to never truly know what the best solution is, and just come up with the best we can.",{"type":22,"tag":23,"props":3891,"children":3892},{},[3893],{"type":27,"value":3894},"With that in mind, let's have another go at comparing the qualities of these two solutions, along with yet another one added to the mix.",{"type":22,"tag":193,"props":3896,"children":3898},{"id":3897},"comparing-solutions",[3899],{"type":27,"value":3900},"Comparing Solutions",{"type":22,"tag":23,"props":3902,"children":3903},{},[3904,3906,3911],{"type":27,"value":3905},"The \"original\" solution is understandable at a glance, it doesn't use any fancy features a junior developer might not understand, it doesn't do any dynamic string allocations, and it's flexible for a few types of future changes (e.g. changing dot colors per stage or per position). On the other hand, it's many lines of code, it's repetitious, and its many ",{"type":22,"tag":486,"props":3907,"children":3909},{"className":3908},[],[3910],{"type":27,"value":2519},{"type":27,"value":3912}," comparisons could be a performance problem if called in a tight loop.",{"type":22,"tag":23,"props":3914,"children":3915},{},[3916,3918,3924],{"type":27,"value":3917},"The \"math\" solution is about as compact as a solution could be, and consequently it's quite elegant too. On the other hand, that makes it harder to read, it's not as flexible for potential future changes, and its multiple ",{"type":22,"tag":486,"props":3919,"children":3921},{"className":3920},[],[3922],{"type":27,"value":3923},"string",{"type":27,"value":3925}," allocations could be its own sort of performance problem.",{"type":22,"tag":23,"props":3927,"children":3928},{},[3929],{"type":27,"value":3930},"The third one is a blend that we can call the \"array\" solution:",{"type":22,"tag":886,"props":3932,"children":3934},{"className":2854,"code":3933,"language":2856,"meta":8,"style":8},"private static readonly string[] Rounds = {\n    \"⚪⚪⚪⚪⚪⚪⚪⚪⚪⚪\",\n    \"🔵⚪⚪⚪⚪⚪⚪⚪⚪⚪\",\n    \"🔵🔵⚪⚪⚪⚪⚪⚪⚪⚪\",\n    \"🔵🔵🔵⚪⚪⚪⚪⚪⚪⚪\",\n    \"🔵🔵🔵🔵⚪⚪⚪⚪⚪⚪\",\n    \"🔵🔵🔵🔵🔵⚪⚪⚪⚪⚪\",\n    \"🔵🔵🔵🔵🔵🔵⚪⚪⚪⚪\",\n    \"🔵🔵🔵🔵🔵🔵🔵⚪⚪⚪\",\n    \"🔵🔵🔵🔵🔵🔵🔵🔵⚪⚪\",\n    \"🔵🔵🔵🔵🔵🔵🔵🔵🔵⚪\",\n    \"🔵🔵🔵🔵🔵🔵🔵🔵🔵🔵\",\n};\n\nprivate static string GetPercentageRounds(double percentage)\n{\n    return Rounds[(int)Math.Ceiling(percentage * (Rounds.Length - 1))];\n}\n",[3935],{"type":22,"tag":486,"props":3936,"children":3937},{"__ignoreMap":8},[3938,3976,3989,4001,4013,4025,4037,4049,4061,4073,4085,4097,4109,4117,4124,4159,4166,4217],{"type":22,"tag":896,"props":3939,"children":3940},{"class":898,"line":899},[3941,3945,3949,3954,3958,3963,3968,3972],{"type":22,"tag":896,"props":3942,"children":3943},{"style":1691},[3944],{"type":27,"value":2868},{"type":22,"tag":896,"props":3946,"children":3947},{"style":1691},[3948],{"type":27,"value":2873},{"type":22,"tag":896,"props":3950,"children":3951},{"style":1691},[3952],{"type":27,"value":3953}," readonly",{"type":22,"tag":896,"props":3955,"children":3956},{"style":1691},[3957],{"type":27,"value":2878},{"type":22,"tag":896,"props":3959,"children":3960},{"style":903},[3961],{"type":27,"value":3962},"[] ",{"type":22,"tag":896,"props":3964,"children":3965},{"style":933},[3966],{"type":27,"value":3967},"Rounds",{"type":22,"tag":896,"props":3969,"children":3970},{"style":1691},[3971],{"type":27,"value":3638},{"type":22,"tag":896,"props":3973,"children":3974},{"style":903},[3975],{"type":27,"value":1626},{"type":22,"tag":896,"props":3977,"children":3978},{"class":898,"line":758},[3979,3984],{"type":22,"tag":896,"props":3980,"children":3981},{"style":944},[3982],{"type":27,"value":3983},"    \"⚪⚪⚪⚪⚪⚪⚪⚪⚪⚪\"",{"type":22,"tag":896,"props":3985,"children":3986},{"style":903},[3987],{"type":27,"value":3988},",\n",{"type":22,"tag":896,"props":3990,"children":3991},{"class":898,"line":755},[3992,3997],{"type":22,"tag":896,"props":3993,"children":3994},{"style":944},[3995],{"type":27,"value":3996},"    \"🔵⚪⚪⚪⚪⚪⚪⚪⚪⚪\"",{"type":22,"tag":896,"props":3998,"children":3999},{"style":903},[4000],{"type":27,"value":3988},{"type":22,"tag":896,"props":4002,"children":4003},{"class":898,"line":983},[4004,4009],{"type":22,"tag":896,"props":4005,"children":4006},{"style":944},[4007],{"type":27,"value":4008},"    \"🔵🔵⚪⚪⚪⚪⚪⚪⚪⚪\"",{"type":22,"tag":896,"props":4010,"children":4011},{"style":903},[4012],{"type":27,"value":3988},{"type":22,"tag":896,"props":4014,"children":4015},{"class":898,"line":1013},[4016,4021],{"type":22,"tag":896,"props":4017,"children":4018},{"style":944},[4019],{"type":27,"value":4020},"    \"🔵🔵🔵⚪⚪⚪⚪⚪⚪⚪\"",{"type":22,"tag":896,"props":4022,"children":4023},{"style":903},[4024],{"type":27,"value":3988},{"type":22,"tag":896,"props":4026,"children":4027},{"class":898,"line":1030},[4028,4033],{"type":22,"tag":896,"props":4029,"children":4030},{"style":944},[4031],{"type":27,"value":4032},"    \"🔵🔵🔵🔵⚪⚪⚪⚪⚪⚪\"",{"type":22,"tag":896,"props":4034,"children":4035},{"style":903},[4036],{"type":27,"value":3988},{"type":22,"tag":896,"props":4038,"children":4039},{"class":898,"line":1068},[4040,4045],{"type":22,"tag":896,"props":4041,"children":4042},{"style":944},[4043],{"type":27,"value":4044},"    \"🔵🔵🔵🔵🔵⚪⚪⚪⚪⚪\"",{"type":22,"tag":896,"props":4046,"children":4047},{"style":903},[4048],{"type":27,"value":3988},{"type":22,"tag":896,"props":4050,"children":4051},{"class":898,"line":1085},[4052,4057],{"type":22,"tag":896,"props":4053,"children":4054},{"style":944},[4055],{"type":27,"value":4056},"    \"🔵🔵🔵🔵🔵🔵⚪⚪⚪⚪\"",{"type":22,"tag":896,"props":4058,"children":4059},{"style":903},[4060],{"type":27,"value":3988},{"type":22,"tag":896,"props":4062,"children":4063},{"class":898,"line":1123},[4064,4069],{"type":22,"tag":896,"props":4065,"children":4066},{"style":944},[4067],{"type":27,"value":4068},"    \"🔵🔵🔵🔵🔵🔵🔵⚪⚪⚪\"",{"type":22,"tag":896,"props":4070,"children":4071},{"style":903},[4072],{"type":27,"value":3988},{"type":22,"tag":896,"props":4074,"children":4075},{"class":898,"line":1161},[4076,4081],{"type":22,"tag":896,"props":4077,"children":4078},{"style":944},[4079],{"type":27,"value":4080},"    \"🔵🔵🔵🔵🔵🔵🔵🔵⚪⚪\"",{"type":22,"tag":896,"props":4082,"children":4083},{"style":903},[4084],{"type":27,"value":3988},{"type":22,"tag":896,"props":4086,"children":4087},{"class":898,"line":2298},[4088,4093],{"type":22,"tag":896,"props":4089,"children":4090},{"style":944},[4091],{"type":27,"value":4092},"    \"🔵🔵🔵🔵🔵🔵🔵🔵🔵⚪\"",{"type":22,"tag":896,"props":4094,"children":4095},{"style":903},[4096],{"type":27,"value":3988},{"type":22,"tag":896,"props":4098,"children":4099},{"class":898,"line":3170},[4100,4105],{"type":22,"tag":896,"props":4101,"children":4102},{"style":944},[4103],{"type":27,"value":4104},"    \"🔵🔵🔵🔵🔵🔵🔵🔵🔵🔵\"",{"type":22,"tag":896,"props":4106,"children":4107},{"style":903},[4108],{"type":27,"value":3988},{"type":22,"tag":896,"props":4110,"children":4111},{"class":898,"line":3187},[4112],{"type":22,"tag":896,"props":4113,"children":4114},{"style":903},[4115],{"type":27,"value":4116},"};\n",{"type":22,"tag":896,"props":4118,"children":4119},{"class":898,"line":3228},[4120],{"type":22,"tag":896,"props":4121,"children":4122},{"emptyLinePlaceholder":3481},[4123],{"type":27,"value":3484},{"type":22,"tag":896,"props":4125,"children":4126},{"class":898,"line":3245},[4127,4131,4135,4139,4143,4147,4151,4155],{"type":22,"tag":896,"props":4128,"children":4129},{"style":1691},[4130],{"type":27,"value":2868},{"type":22,"tag":896,"props":4132,"children":4133},{"style":1691},[4134],{"type":27,"value":2873},{"type":22,"tag":896,"props":4136,"children":4137},{"style":1691},[4138],{"type":27,"value":2878},{"type":22,"tag":896,"props":4140,"children":4141},{"style":933},[4142],{"type":27,"value":2883},{"type":22,"tag":896,"props":4144,"children":4145},{"style":903},[4146],{"type":27,"value":2888},{"type":22,"tag":896,"props":4148,"children":4149},{"style":1691},[4150],{"type":27,"value":2893},{"type":22,"tag":896,"props":4152,"children":4153},{"style":933},[4154],{"type":27,"value":2898},{"type":22,"tag":896,"props":4156,"children":4157},{"style":903},[4158],{"type":27,"value":2903},{"type":22,"tag":896,"props":4160,"children":4161},{"class":898,"line":3286},[4162],{"type":22,"tag":896,"props":4163,"children":4164},{"style":903},[4165],{"type":27,"value":2571},{"type":22,"tag":896,"props":4167,"children":4168},{"class":898,"line":3303},[4169,4173,4178,4182,4186,4190,4194,4198,4203,4208,4212],{"type":22,"tag":896,"props":4170,"children":4171},{"style":1691},[4172],{"type":27,"value":3493},{"type":22,"tag":896,"props":4174,"children":4175},{"style":903},[4176],{"type":27,"value":4177}," Rounds[(",{"type":22,"tag":896,"props":4179,"children":4180},{"style":1691},[4181],{"type":27,"value":3648},{"type":22,"tag":896,"props":4183,"children":4184},{"style":903},[4185],{"type":27,"value":3653},{"type":22,"tag":896,"props":4187,"children":4188},{"style":933},[4189],{"type":27,"value":3658},{"type":22,"tag":896,"props":4191,"children":4192},{"style":903},[4193],{"type":27,"value":3663},{"type":22,"tag":896,"props":4195,"children":4196},{"style":1691},[4197],{"type":27,"value":3668},{"type":22,"tag":896,"props":4199,"children":4200},{"style":903},[4201],{"type":27,"value":4202}," (Rounds.Length ",{"type":22,"tag":896,"props":4204,"children":4205},{"style":1691},[4206],{"type":27,"value":4207},"-",{"type":22,"tag":896,"props":4209,"children":4210},{"style":1632},[4211],{"type":27,"value":1688},{"type":22,"tag":896,"props":4213,"children":4214},{"style":903},[4215],{"type":27,"value":4216},"))];\n",{"type":22,"tag":896,"props":4218,"children":4219},{"class":898,"line":3344},[4220],{"type":22,"tag":896,"props":4221,"children":4222},{"style":903},[4223],{"type":27,"value":1745},{"type":22,"tag":23,"props":4225,"children":4226},{},[4227,4229,4234,4236,4242,4244,4251],{"type":27,"value":4228},"This one has the same at-a-glance readability of the original solution, it's even more flexible since it works with an arbitrary number of loading stages, and it makes no dynamic ",{"type":22,"tag":486,"props":4230,"children":4232},{"className":4231},[],[4233],{"type":27,"value":3923},{"type":27,"value":4235}," allocations. On the other hand, it requires a separate ",{"type":22,"tag":486,"props":4237,"children":4239},{"className":4238},[],[4240],{"type":27,"value":4241},"static readonly string[]",{"type":27,"value":4243}," that's external to the method (hampering readability), and its main advantage over the simpler original or \"math\" solutions is just performance, something which is likely a ",{"type":22,"tag":30,"props":4245,"children":4248},{"href":4246,"rel":4247},"https://wiki.c2.com/?PrematureOptimization",[34],[4249],{"type":27,"value":4250},"premature optimization",{"type":27,"value":4252}," for a method like this (particularly since we haven't actually measured to confirm that it is faster).",{"type":22,"tag":193,"props":4254,"children":4256},{"id":4255},"so-which-solution-is-best",[4257],{"type":27,"value":4258},"So, Which Solution Is Best?",{"type":22,"tag":23,"props":4260,"children":4261},{},[4262],{"type":27,"value":4263},"None of them and all of them, I would argue. A developer might have their own preference, but none of the solutions are truly so bad that they ought to be condemned in a code review.",{"type":22,"tag":23,"props":4265,"children":4266},{},[4267,4269,4275,4277,4283],{"type":27,"value":4268},"The debate over this snippet of code reminds me how hard software development can be: here's a piece of code which is trivial enough that debating it is almost certainly ",{"type":22,"tag":30,"props":4270,"children":4272},{"href":2607,"rel":4271},[34],[4273],{"type":27,"value":4274},"bike-shedding",{"type":27,"value":4276},", but even so, that debate demonstrates just how many decisions a developer needs to make even during the easiest parts of their day. Best to accept that, for trivial code like ",{"type":22,"tag":486,"props":4278,"children":4280},{"className":4279},[],[4281],{"type":27,"value":4282},"GetPercentageRounds()",{"type":27,"value":4284},", maybe an embarrassingly simple and repetitious solution is just fine, and to save our energy for debating the solutions to the complex and mission-critical problems where the subtleties are more impactful.",{"type":22,"tag":1999,"props":4286,"children":4287},{},[4288],{"type":27,"value":2457},{"title":8,"searchDepth":755,"depth":755,"links":4290},[4291,4292,4293],{"id":3798,"depth":758,"text":3801},{"id":3897,"depth":758,"text":3900},{"id":4255,"depth":758,"text":4258},"content:phendry:2023-01-19:BadCode.md","phendry/2023-01-19/BadCode.md","phendry/2023-01-19/BadCode",{"user":774,"name":775},{"_path":4299,"_dir":4300,"_draft":7,"_partial":7,"_locale":8,"title":4301,"description":4302,"image":4303,"tags":4304,"publishDate":4305,"excerpt":4302,"body":4306,"_type":767,"_id":11994,"_source":769,"_file":11995,"_stem":11996,"_extension":772,"author":11997},"/phendry/2022-07-28/migratingfromexpresstofastifypart2","2022-07-28","Migrating from Express to Fastify, Part 2","In Part 1, we looked at the features of the Fastify Node.js Web framework compared to Express.js. In Part 2, we'll work through migrating an example Express.js application to Fastify.","/phendry/2022-07-28/img/Migrating from Express to Fastify, Part 2.png",[2507,2819],"2023-12-31",{"type":19,"children":4307,"toc":11981},[4308,4331,4358,4372,4378,4396,4401,4433,4439,4452,4547,4566,4572,4577,4587,4608,4643,4655,4668,4897,4902,5329,5334,5451,5472,5477,5505,6142,6155,6660,6686,6728,6741,6747,6753,6773,6802,6811,7646,7658,7788,7817,7823,7836,8373,8379,8399,8409,8507,8528,8537,8692,8713,8718,8727,9190,9195,9204,9545,9572,9608,9617,9887,9892,10137,10142,10154,10160,10202,10214,10220,10225,10230,10751,10772,10782,11943,11949,11961,11966,11977],{"type":22,"tag":23,"props":4309,"children":4310},{},[4311,4313,4320,4322,4329],{"type":27,"value":4312},"In Part 1, we looked at the features of the ",{"type":22,"tag":30,"props":4314,"children":4317},{"href":4315,"rel":4316},"https://www.fastify.io/",[34],[4318],{"type":27,"value":4319},"Fastify",{"type":27,"value":4321}," Node.js Web framework compared to ",{"type":22,"tag":30,"props":4323,"children":4326},{"href":4324,"rel":4325},"https://expressjs.com/",[34],[4327],{"type":27,"value":4328},"Express.js",{"type":27,"value":4330},". In Part 2, we'll work through migrating an example Express.js application to Fastify.",{"type":22,"tag":23,"props":4332,"children":4333},{},[4334,4336,4347,4349,4356],{"type":27,"value":4335},"The example application we'll migrate is called ",{"type":22,"tag":30,"props":4337,"children":4340},{"href":4338,"rel":4339},"https://github.com/gothinkster/node-express-realworld-example-app",[34],[4341],{"type":22,"tag":486,"props":4342,"children":4344},{"className":4343},[],[4345],{"type":27,"value":4346},"node-express-realworld-example-app",{"type":27,"value":4348},", which is an Express/MongoDB-based implementation of the ",{"type":22,"tag":30,"props":4350,"children":4353},{"href":4351,"rel":4352},"https://github.com/gothinkster/realworld",[34],[4354],{"type":27,"value":4355},"RealWorld example app",{"type":27,"value":4357}," backend. RealWorld is a specification of a simple blogging website, so the backend component involves managing users, articles, and comments; it's a good demonstration project since it's just large enough to be a decent representation of a production project.",{"type":22,"tag":23,"props":4359,"children":4360},{},[4361,4363,4370],{"type":27,"value":4362},"The code for this example can be found ",{"type":22,"tag":30,"props":4364,"children":4367},{"href":4365,"rel":4366},"https://github.com/artandlogic/node-express-to-fastify-example",[34],[4368],{"type":27,"value":4369},"here",{"type":27,"value":4371},"; at each step in the migration, there'll be a link to follow along with the complete set of changes.",{"type":22,"tag":193,"props":4373,"children":4375},{"id":4374},"incremental-migration-using-fastifyexpress",[4376],{"type":27,"value":4377},"Incremental migration using @fastify/express",{"type":22,"tag":23,"props":4379,"children":4380},{},[4381,4383,4394],{"type":27,"value":4382},"Any code refactor is best done incrementally, so that it can be tested every step along the way. Fastify provides a core plugin called ",{"type":22,"tag":30,"props":4384,"children":4387},{"href":4385,"rel":4386},"https://github.com/fastify/fastify-express",[34],[4388],{"type":22,"tag":486,"props":4389,"children":4391},{"className":4390},[],[4392],{"type":27,"value":4393},"@fastify/express",{"type":27,"value":4395}," which makes this possible during an Express-to-Fastify migration. It can be used in a few different ways, but the feature that's useful in this example is its ability to load an entire Express application inside of a Fastify application.",{"type":22,"tag":23,"props":4397,"children":4398},{},[4399],{"type":27,"value":4400},"With this capability, an Express application can be migrated to Fastify in a three-step process:",{"type":22,"tag":847,"props":4402,"children":4403},{},[4404,4416,4421],{"type":22,"tag":115,"props":4405,"children":4406},{},[4407,4409,4414],{"type":27,"value":4408},"Replace the Express server component with Fastify, using ",{"type":22,"tag":486,"props":4410,"children":4412},{"className":4411},[],[4413],{"type":27,"value":4393},{"type":27,"value":4415}," to load the existing Express router",{"type":22,"tag":115,"props":4417,"children":4418},{},[4419],{"type":27,"value":4420},"Migrate routes from Express to Fastify, one by one",{"type":22,"tag":115,"props":4422,"children":4423},{},[4424,4426,4431],{"type":27,"value":4425},"Remove Express, associated plugins, and ",{"type":22,"tag":486,"props":4427,"children":4429},{"className":4428},[],[4430],{"type":27,"value":4393},{"type":27,"value":4432}," from the project",{"type":22,"tag":193,"props":4434,"children":4436},{"id":4435},"an-overview-of-the-example-project",[4437],{"type":27,"value":4438},"An overview of the example project",{"type":22,"tag":23,"props":4440,"children":4441},{},[4442,4444,4450],{"type":27,"value":4443},"The initial state of the project can be browsed ",{"type":22,"tag":30,"props":4445,"children":4448},{"href":4446,"rel":4447},"https://github.com/artandlogic/node-express-to-fastify-example/tree/3614fd4aeeca2c863c79b9127ecf467ce8c80fe2",[34],[4449],{"type":27,"value":4369},{"type":27,"value":4451},". It's structured in a straightforward way:",{"type":22,"tag":111,"props":4453,"children":4454},{},[4455,4474,4494,4505,4516,4536],{"type":22,"tag":115,"props":4456,"children":4457},{},[4458,4464,4466,4472],{"type":22,"tag":486,"props":4459,"children":4461},{"className":4460},[],[4462],{"type":27,"value":4463},"config/",{"type":27,"value":4465},": A few modules for configuring environment variables and the ",{"type":22,"tag":486,"props":4467,"children":4469},{"className":4468},[],[4470],{"type":27,"value":4471},"passport",{"type":27,"value":4473}," library used for authentication",{"type":22,"tag":115,"props":4475,"children":4476},{},[4477,4483,4485,4492],{"type":22,"tag":486,"props":4478,"children":4480},{"className":4479},[],[4481],{"type":27,"value":4482},"models/",{"type":27,"value":4484},": Contains the ",{"type":22,"tag":30,"props":4486,"children":4489},{"href":4487,"rel":4488},"https://mongoosejs.com/",[34],[4490],{"type":27,"value":4491},"Mongoose",{"type":27,"value":4493}," models used for interfacing with MongoDB",{"type":22,"tag":115,"props":4495,"children":4496},{},[4497,4503],{"type":22,"tag":486,"props":4498,"children":4500},{"className":4499},[],[4501],{"type":27,"value":4502},"public/",{"type":27,"value":4504},": A directory for static assets",{"type":22,"tag":115,"props":4506,"children":4507},{},[4508,4514],{"type":22,"tag":486,"props":4509,"children":4511},{"className":4510},[],[4512],{"type":27,"value":4513},"routes/",{"type":27,"value":4515},": Modules defining the API endpoints of the application",{"type":22,"tag":115,"props":4517,"children":4518},{},[4519,4525,4527,4534],{"type":22,"tag":486,"props":4520,"children":4522},{"className":4521},[],[4523],{"type":27,"value":4524},"tests/",{"type":27,"value":4526},": Contains ",{"type":22,"tag":30,"props":4528,"children":4531},{"href":4529,"rel":4530},"https://learning.postman.com/docs/running-collections/using-newman-cli/command-line-integration-with-newman/",[34],[4532],{"type":27,"value":4533},"Newman",{"type":27,"value":4535},"-based end-to-end automated testing",{"type":22,"tag":115,"props":4537,"children":4538},{},[4539,4545],{"type":22,"tag":486,"props":4540,"children":4542},{"className":4541},[],[4543],{"type":27,"value":4544},"app.js",{"type":27,"value":4546},": The main entrypoint where the Express server is started",{"type":22,"tag":23,"props":4548,"children":4549},{},[4550,4552,4557,4559,4564],{"type":27,"value":4551},"The first step in the migration will be to change ",{"type":22,"tag":486,"props":4553,"children":4555},{"className":4554},[],[4556],{"type":27,"value":4544},{"type":27,"value":4558}," such that it starts a Fastify server, and using the ",{"type":22,"tag":486,"props":4560,"children":4562},{"className":4561},[],[4563],{"type":27,"value":4393},{"type":27,"value":4565}," plugin in order to load the existing Express application. The end result is an application that works no differently than before, but it'll be ready for Fastify routes to be added which co-exist with the Express ones.",{"type":22,"tag":193,"props":4567,"children":4569},{"id":4568},"step-1-replacing-the-server-component",[4570],{"type":27,"value":4571},"Step 1: Replacing the server component",{"type":22,"tag":23,"props":4573,"children":4574},{},[4575],{"type":27,"value":4576},"First, a few new packages need to be installed:",{"type":22,"tag":886,"props":4578,"children":4582},{"className":4579,"code":4581,"language":27},[4580],"language-text","npm install --save fastify @fastify/express fastify-plugin @fastify/formbody\n",[4583],{"type":22,"tag":486,"props":4584,"children":4585},{"__ignoreMap":8},[4586],{"type":27,"value":4581},{"type":22,"tag":23,"props":4588,"children":4589},{},[4590,4592,4598,4600,4606],{"type":27,"value":4591},"Note that ",{"type":22,"tag":486,"props":4593,"children":4595},{"className":4594},[],[4596],{"type":27,"value":4597},"fastify-plugin",{"type":27,"value":4599}," is a utility we'll use for defining Fastify plugins, and ",{"type":22,"tag":486,"props":4601,"children":4603},{"className":4602},[],[4604],{"type":27,"value":4605},"@fastify/formbody",{"type":27,"value":4607}," is for an Express-related workaround we'll discuss later.",{"type":22,"tag":23,"props":4609,"children":4610},{},[4611,4613,4618,4620,4626,4628,4634,4636,4641],{"type":27,"value":4612},"Next, we'll want to move the existing Express routes from ",{"type":22,"tag":486,"props":4614,"children":4616},{"className":4615},[],[4617],{"type":27,"value":4513},{"type":27,"value":4619}," into a new ",{"type":22,"tag":486,"props":4621,"children":4623},{"className":4622},[],[4624],{"type":27,"value":4625},"routes/legacy/",{"type":27,"value":4627}," directory, updating ",{"type":22,"tag":486,"props":4629,"children":4631},{"className":4630},[],[4632],{"type":27,"value":4633},"require()",{"type":27,"value":4635}," statements accordingly. This leaves the ",{"type":22,"tag":486,"props":4637,"children":4639},{"className":4638},[],[4640],{"type":27,"value":4513},{"type":27,"value":4642}," directory ready to accept new Fastify routes in step 2.",{"type":22,"tag":23,"props":4644,"children":4645},{},[4646,4648,4653],{"type":27,"value":4647},"With those preparations complete, it's time to replace the Express server in ",{"type":22,"tag":486,"props":4649,"children":4651},{"className":4650},[],[4652],{"type":27,"value":4544},{"type":27,"value":4654}," with a Fastify server. Currently, what that module is doing is:",{"type":22,"tag":23,"props":4656,"children":4657},{},[4658,4660,4666],{"type":27,"value":4659},"Instantiating an ",{"type":22,"tag":486,"props":4661,"children":4663},{"className":4662},[],[4664],{"type":27,"value":4665},"Express",{"type":27,"value":4667}," object...",{"type":22,"tag":886,"props":4669,"children":4671},{"className":2505,"code":4670,"language":2507,"meta":8,"style":8},"var express = require('express');\nvar bodyParser = require('body-parser');\nvar cors = require('cors');\nvar mongoose = require('mongoose');\n/* ... */\n\nvar isProduction = process.env.NODE_ENV === 'production';\n\nvar app = express();\n",[4672],{"type":22,"tag":486,"props":4673,"children":4674},{"__ignoreMap":8},[4675,4710,4743,4776,4809,4817,4824,4864,4871],{"type":22,"tag":896,"props":4676,"children":4677},{"class":898,"line":899},[4678,4683,4688,4692,4697,4701,4706],{"type":22,"tag":896,"props":4679,"children":4680},{"style":1691},[4681],{"type":27,"value":4682},"var",{"type":22,"tag":896,"props":4684,"children":4685},{"style":903},[4686],{"type":27,"value":4687}," express ",{"type":22,"tag":896,"props":4689,"children":4690},{"style":1691},[4691],{"type":27,"value":941},{"type":22,"tag":896,"props":4693,"children":4694},{"style":933},[4695],{"type":27,"value":4696}," require",{"type":22,"tag":896,"props":4698,"children":4699},{"style":903},[4700],{"type":27,"value":2888},{"type":22,"tag":896,"props":4702,"children":4703},{"style":944},[4704],{"type":27,"value":4705},"'express'",{"type":22,"tag":896,"props":4707,"children":4708},{"style":903},[4709],{"type":27,"value":3678},{"type":22,"tag":896,"props":4711,"children":4712},{"class":898,"line":758},[4713,4717,4722,4726,4730,4734,4739],{"type":22,"tag":896,"props":4714,"children":4715},{"style":1691},[4716],{"type":27,"value":4682},{"type":22,"tag":896,"props":4718,"children":4719},{"style":903},[4720],{"type":27,"value":4721}," bodyParser ",{"type":22,"tag":896,"props":4723,"children":4724},{"style":1691},[4725],{"type":27,"value":941},{"type":22,"tag":896,"props":4727,"children":4728},{"style":933},[4729],{"type":27,"value":4696},{"type":22,"tag":896,"props":4731,"children":4732},{"style":903},[4733],{"type":27,"value":2888},{"type":22,"tag":896,"props":4735,"children":4736},{"style":944},[4737],{"type":27,"value":4738},"'body-parser'",{"type":22,"tag":896,"props":4740,"children":4741},{"style":903},[4742],{"type":27,"value":3678},{"type":22,"tag":896,"props":4744,"children":4745},{"class":898,"line":755},[4746,4750,4755,4759,4763,4767,4772],{"type":22,"tag":896,"props":4747,"children":4748},{"style":1691},[4749],{"type":27,"value":4682},{"type":22,"tag":896,"props":4751,"children":4752},{"style":903},[4753],{"type":27,"value":4754}," cors ",{"type":22,"tag":896,"props":4756,"children":4757},{"style":1691},[4758],{"type":27,"value":941},{"type":22,"tag":896,"props":4760,"children":4761},{"style":933},[4762],{"type":27,"value":4696},{"type":22,"tag":896,"props":4764,"children":4765},{"style":903},[4766],{"type":27,"value":2888},{"type":22,"tag":896,"props":4768,"children":4769},{"style":944},[4770],{"type":27,"value":4771},"'cors'",{"type":22,"tag":896,"props":4773,"children":4774},{"style":903},[4775],{"type":27,"value":3678},{"type":22,"tag":896,"props":4777,"children":4778},{"class":898,"line":983},[4779,4783,4788,4792,4796,4800,4805],{"type":22,"tag":896,"props":4780,"children":4781},{"style":1691},[4782],{"type":27,"value":4682},{"type":22,"tag":896,"props":4784,"children":4785},{"style":903},[4786],{"type":27,"value":4787}," mongoose ",{"type":22,"tag":896,"props":4789,"children":4790},{"style":1691},[4791],{"type":27,"value":941},{"type":22,"tag":896,"props":4793,"children":4794},{"style":933},[4795],{"type":27,"value":4696},{"type":22,"tag":896,"props":4797,"children":4798},{"style":903},[4799],{"type":27,"value":2888},{"type":22,"tag":896,"props":4801,"children":4802},{"style":944},[4803],{"type":27,"value":4804},"'mongoose'",{"type":22,"tag":896,"props":4806,"children":4807},{"style":903},[4808],{"type":27,"value":3678},{"type":22,"tag":896,"props":4810,"children":4811},{"class":898,"line":1013},[4812],{"type":22,"tag":896,"props":4813,"children":4814},{"style":2070},[4815],{"type":27,"value":4816},"/* ... */\n",{"type":22,"tag":896,"props":4818,"children":4819},{"class":898,"line":1030},[4820],{"type":22,"tag":896,"props":4821,"children":4822},{"emptyLinePlaceholder":3481},[4823],{"type":27,"value":3484},{"type":22,"tag":896,"props":4825,"children":4826},{"class":898,"line":1068},[4827,4831,4836,4840,4845,4850,4855,4860],{"type":22,"tag":896,"props":4828,"children":4829},{"style":1691},[4830],{"type":27,"value":4682},{"type":22,"tag":896,"props":4832,"children":4833},{"style":903},[4834],{"type":27,"value":4835}," isProduction ",{"type":22,"tag":896,"props":4837,"children":4838},{"style":1691},[4839],{"type":27,"value":941},{"type":22,"tag":896,"props":4841,"children":4842},{"style":903},[4843],{"type":27,"value":4844}," process.env.",{"type":22,"tag":896,"props":4846,"children":4847},{"style":1632},[4848],{"type":27,"value":4849},"NODE_ENV",{"type":22,"tag":896,"props":4851,"children":4852},{"style":1691},[4853],{"type":27,"value":4854}," ===",{"type":22,"tag":896,"props":4856,"children":4857},{"style":944},[4858],{"type":27,"value":4859}," 'production'",{"type":22,"tag":896,"props":4861,"children":4862},{"style":903},[4863],{"type":27,"value":1649},{"type":22,"tag":896,"props":4865,"children":4866},{"class":898,"line":1085},[4867],{"type":22,"tag":896,"props":4868,"children":4869},{"emptyLinePlaceholder":3481},[4870],{"type":27,"value":3484},{"type":22,"tag":896,"props":4872,"children":4873},{"class":898,"line":1123},[4874,4878,4883,4887,4892],{"type":22,"tag":896,"props":4875,"children":4876},{"style":1691},[4877],{"type":27,"value":4682},{"type":22,"tag":896,"props":4879,"children":4880},{"style":903},[4881],{"type":27,"value":4882}," app ",{"type":22,"tag":896,"props":4884,"children":4885},{"style":1691},[4886],{"type":27,"value":941},{"type":22,"tag":896,"props":4888,"children":4889},{"style":933},[4890],{"type":27,"value":4891}," express",{"type":22,"tag":896,"props":4893,"children":4894},{"style":903},[4895],{"type":27,"value":4896},"();\n",{"type":22,"tag":23,"props":4898,"children":4899},{},[4900],{"type":27,"value":4901},"...configuring it, applying middleware, and setting up a database connection...",{"type":22,"tag":886,"props":4903,"children":4905},{"className":2505,"code":4904,"language":2507,"meta":8,"style":8},"app.use(cors());\n\napp.use(require('morgan')('dev'));\napp.use(bodyParser.urlencoded({ extended: false }));\napp.use(bodyParser.json());\n\n/* ... */\n\nif(isProduction){\n  mongoose.connect(process.env.MONGODB_URI);\n} else {\n  mongoose.connect('mongodb://localhost/conduit');\n  mongoose.set('debug', true);\n}\n\nrequire('./models/User');\nrequire('./models/Article');\nrequire('./models/Comment');\nrequire('./config/passport');\n\napp.use(require('./routes'));\n\n/* ... */\n",[4906],{"type":22,"tag":486,"props":4907,"children":4908},{"__ignoreMap":8},[4909,4936,4943,4987,5023,5047,5054,5061,5068,5080,5107,5124,5148,5182,5189,5196,5216,5236,5256,5276,5283,5315,5322],{"type":22,"tag":896,"props":4910,"children":4911},{"class":898,"line":899},[4912,4917,4922,4926,4931],{"type":22,"tag":896,"props":4913,"children":4914},{"style":903},[4915],{"type":27,"value":4916},"app.",{"type":22,"tag":896,"props":4918,"children":4919},{"style":933},[4920],{"type":27,"value":4921},"use",{"type":22,"tag":896,"props":4923,"children":4924},{"style":903},[4925],{"type":27,"value":2888},{"type":22,"tag":896,"props":4927,"children":4928},{"style":933},[4929],{"type":27,"value":4930},"cors",{"type":22,"tag":896,"props":4932,"children":4933},{"style":903},[4934],{"type":27,"value":4935},"());\n",{"type":22,"tag":896,"props":4937,"children":4938},{"class":898,"line":758},[4939],{"type":22,"tag":896,"props":4940,"children":4941},{"emptyLinePlaceholder":3481},[4942],{"type":27,"value":3484},{"type":22,"tag":896,"props":4944,"children":4945},{"class":898,"line":755},[4946,4950,4954,4958,4963,4967,4972,4977,4982],{"type":22,"tag":896,"props":4947,"children":4948},{"style":903},[4949],{"type":27,"value":4916},{"type":22,"tag":896,"props":4951,"children":4952},{"style":933},[4953],{"type":27,"value":4921},{"type":22,"tag":896,"props":4955,"children":4956},{"style":903},[4957],{"type":27,"value":2888},{"type":22,"tag":896,"props":4959,"children":4960},{"style":933},[4961],{"type":27,"value":4962},"require",{"type":22,"tag":896,"props":4964,"children":4965},{"style":903},[4966],{"type":27,"value":2888},{"type":22,"tag":896,"props":4968,"children":4969},{"style":944},[4970],{"type":27,"value":4971},"'morgan'",{"type":22,"tag":896,"props":4973,"children":4974},{"style":903},[4975],{"type":27,"value":4976},")(",{"type":22,"tag":896,"props":4978,"children":4979},{"style":944},[4980],{"type":27,"value":4981},"'dev'",{"type":22,"tag":896,"props":4983,"children":4984},{"style":903},[4985],{"type":27,"value":4986},"));\n",{"type":22,"tag":896,"props":4988,"children":4989},{"class":898,"line":983},[4990,4994,4998,5003,5008,5013,5018],{"type":22,"tag":896,"props":4991,"children":4992},{"style":903},[4993],{"type":27,"value":4916},{"type":22,"tag":896,"props":4995,"children":4996},{"style":933},[4997],{"type":27,"value":4921},{"type":22,"tag":896,"props":4999,"children":5000},{"style":903},[5001],{"type":27,"value":5002},"(bodyParser.",{"type":22,"tag":896,"props":5004,"children":5005},{"style":933},[5006],{"type":27,"value":5007},"urlencoded",{"type":22,"tag":896,"props":5009,"children":5010},{"style":903},[5011],{"type":27,"value":5012},"({ extended: ",{"type":22,"tag":896,"props":5014,"children":5015},{"style":1632},[5016],{"type":27,"value":5017},"false",{"type":22,"tag":896,"props":5019,"children":5020},{"style":903},[5021],{"type":27,"value":5022}," }));\n",{"type":22,"tag":896,"props":5024,"children":5025},{"class":898,"line":1013},[5026,5030,5034,5038,5043],{"type":22,"tag":896,"props":5027,"children":5028},{"style":903},[5029],{"type":27,"value":4916},{"type":22,"tag":896,"props":5031,"children":5032},{"style":933},[5033],{"type":27,"value":4921},{"type":22,"tag":896,"props":5035,"children":5036},{"style":903},[5037],{"type":27,"value":5002},{"type":22,"tag":896,"props":5039,"children":5040},{"style":933},[5041],{"type":27,"value":5042},"json",{"type":22,"tag":896,"props":5044,"children":5045},{"style":903},[5046],{"type":27,"value":4935},{"type":22,"tag":896,"props":5048,"children":5049},{"class":898,"line":1030},[5050],{"type":22,"tag":896,"props":5051,"children":5052},{"emptyLinePlaceholder":3481},[5053],{"type":27,"value":3484},{"type":22,"tag":896,"props":5055,"children":5056},{"class":898,"line":1068},[5057],{"type":22,"tag":896,"props":5058,"children":5059},{"style":2070},[5060],{"type":27,"value":4816},{"type":22,"tag":896,"props":5062,"children":5063},{"class":898,"line":1085},[5064],{"type":22,"tag":896,"props":5065,"children":5066},{"emptyLinePlaceholder":3481},[5067],{"type":27,"value":3484},{"type":22,"tag":896,"props":5069,"children":5070},{"class":898,"line":1123},[5071,5075],{"type":22,"tag":896,"props":5072,"children":5073},{"style":1691},[5074],{"type":27,"value":2519},{"type":22,"tag":896,"props":5076,"children":5077},{"style":903},[5078],{"type":27,"value":5079},"(isProduction){\n",{"type":22,"tag":896,"props":5081,"children":5082},{"class":898,"line":1161},[5083,5088,5093,5098,5103],{"type":22,"tag":896,"props":5084,"children":5085},{"style":903},[5086],{"type":27,"value":5087},"  mongoose.",{"type":22,"tag":896,"props":5089,"children":5090},{"style":933},[5091],{"type":27,"value":5092},"connect",{"type":22,"tag":896,"props":5094,"children":5095},{"style":903},[5096],{"type":27,"value":5097},"(process.env.",{"type":22,"tag":896,"props":5099,"children":5100},{"style":1632},[5101],{"type":27,"value":5102},"MONGODB_URI",{"type":22,"tag":896,"props":5104,"children":5105},{"style":903},[5106],{"type":27,"value":3678},{"type":22,"tag":896,"props":5108,"children":5109},{"class":898,"line":2298},[5110,5115,5120],{"type":22,"tag":896,"props":5111,"children":5112},{"style":903},[5113],{"type":27,"value":5114},"} ",{"type":22,"tag":896,"props":5116,"children":5117},{"style":1691},[5118],{"type":27,"value":5119},"else",{"type":22,"tag":896,"props":5121,"children":5122},{"style":903},[5123],{"type":27,"value":1626},{"type":22,"tag":896,"props":5125,"children":5126},{"class":898,"line":3170},[5127,5131,5135,5139,5144],{"type":22,"tag":896,"props":5128,"children":5129},{"style":903},[5130],{"type":27,"value":5087},{"type":22,"tag":896,"props":5132,"children":5133},{"style":933},[5134],{"type":27,"value":5092},{"type":22,"tag":896,"props":5136,"children":5137},{"style":903},[5138],{"type":27,"value":2888},{"type":22,"tag":896,"props":5140,"children":5141},{"style":944},[5142],{"type":27,"value":5143},"'mongodb://localhost/conduit'",{"type":22,"tag":896,"props":5145,"children":5146},{"style":903},[5147],{"type":27,"value":3678},{"type":22,"tag":896,"props":5149,"children":5150},{"class":898,"line":3187},[5151,5155,5160,5164,5169,5173,5178],{"type":22,"tag":896,"props":5152,"children":5153},{"style":903},[5154],{"type":27,"value":5087},{"type":22,"tag":896,"props":5156,"children":5157},{"style":933},[5158],{"type":27,"value":5159},"set",{"type":22,"tag":896,"props":5161,"children":5162},{"style":903},[5163],{"type":27,"value":2888},{"type":22,"tag":896,"props":5165,"children":5166},{"style":944},[5167],{"type":27,"value":5168},"'debug'",{"type":22,"tag":896,"props":5170,"children":5171},{"style":903},[5172],{"type":27,"value":236},{"type":22,"tag":896,"props":5174,"children":5175},{"style":1632},[5176],{"type":27,"value":5177},"true",{"type":22,"tag":896,"props":5179,"children":5180},{"style":903},[5181],{"type":27,"value":3678},{"type":22,"tag":896,"props":5183,"children":5184},{"class":898,"line":3228},[5185],{"type":22,"tag":896,"props":5186,"children":5187},{"style":903},[5188],{"type":27,"value":1745},{"type":22,"tag":896,"props":5190,"children":5191},{"class":898,"line":3245},[5192],{"type":22,"tag":896,"props":5193,"children":5194},{"emptyLinePlaceholder":3481},[5195],{"type":27,"value":3484},{"type":22,"tag":896,"props":5197,"children":5198},{"class":898,"line":3286},[5199,5203,5207,5212],{"type":22,"tag":896,"props":5200,"children":5201},{"style":933},[5202],{"type":27,"value":4962},{"type":22,"tag":896,"props":5204,"children":5205},{"style":903},[5206],{"type":27,"value":2888},{"type":22,"tag":896,"props":5208,"children":5209},{"style":944},[5210],{"type":27,"value":5211},"'./models/User'",{"type":22,"tag":896,"props":5213,"children":5214},{"style":903},[5215],{"type":27,"value":3678},{"type":22,"tag":896,"props":5217,"children":5218},{"class":898,"line":3303},[5219,5223,5227,5232],{"type":22,"tag":896,"props":5220,"children":5221},{"style":933},[5222],{"type":27,"value":4962},{"type":22,"tag":896,"props":5224,"children":5225},{"style":903},[5226],{"type":27,"value":2888},{"type":22,"tag":896,"props":5228,"children":5229},{"style":944},[5230],{"type":27,"value":5231},"'./models/Article'",{"type":22,"tag":896,"props":5233,"children":5234},{"style":903},[5235],{"type":27,"value":3678},{"type":22,"tag":896,"props":5237,"children":5238},{"class":898,"line":3344},[5239,5243,5247,5252],{"type":22,"tag":896,"props":5240,"children":5241},{"style":933},[5242],{"type":27,"value":4962},{"type":22,"tag":896,"props":5244,"children":5245},{"style":903},[5246],{"type":27,"value":2888},{"type":22,"tag":896,"props":5248,"children":5249},{"style":944},[5250],{"type":27,"value":5251},"'./models/Comment'",{"type":22,"tag":896,"props":5253,"children":5254},{"style":903},[5255],{"type":27,"value":3678},{"type":22,"tag":896,"props":5257,"children":5258},{"class":898,"line":3361},[5259,5263,5267,5272],{"type":22,"tag":896,"props":5260,"children":5261},{"style":933},[5262],{"type":27,"value":4962},{"type":22,"tag":896,"props":5264,"children":5265},{"style":903},[5266],{"type":27,"value":2888},{"type":22,"tag":896,"props":5268,"children":5269},{"style":944},[5270],{"type":27,"value":5271},"'./config/passport'",{"type":22,"tag":896,"props":5273,"children":5274},{"style":903},[5275],{"type":27,"value":3678},{"type":22,"tag":896,"props":5277,"children":5278},{"class":898,"line":3402},[5279],{"type":22,"tag":896,"props":5280,"children":5281},{"emptyLinePlaceholder":3481},[5282],{"type":27,"value":3484},{"type":22,"tag":896,"props":5284,"children":5285},{"class":898,"line":3419},[5286,5290,5294,5298,5302,5306,5311],{"type":22,"tag":896,"props":5287,"children":5288},{"style":903},[5289],{"type":27,"value":4916},{"type":22,"tag":896,"props":5291,"children":5292},{"style":933},[5293],{"type":27,"value":4921},{"type":22,"tag":896,"props":5295,"children":5296},{"style":903},[5297],{"type":27,"value":2888},{"type":22,"tag":896,"props":5299,"children":5300},{"style":933},[5301],{"type":27,"value":4962},{"type":22,"tag":896,"props":5303,"children":5304},{"style":903},[5305],{"type":27,"value":2888},{"type":22,"tag":896,"props":5307,"children":5308},{"style":944},[5309],{"type":27,"value":5310},"'./routes'",{"type":22,"tag":896,"props":5312,"children":5313},{"style":903},[5314],{"type":27,"value":4986},{"type":22,"tag":896,"props":5316,"children":5317},{"class":898,"line":3460},[5318],{"type":22,"tag":896,"props":5319,"children":5320},{"emptyLinePlaceholder":3481},[5321],{"type":27,"value":3484},{"type":22,"tag":896,"props":5323,"children":5324},{"class":898,"line":3477},[5325],{"type":22,"tag":896,"props":5326,"children":5327},{"style":2070},[5328],{"type":27,"value":4816},{"type":22,"tag":23,"props":5330,"children":5331},{},[5332],{"type":27,"value":5333},"...and finally, starting the server:",{"type":22,"tag":886,"props":5335,"children":5337},{"className":2505,"code":5336,"language":2507,"meta":8,"style":8},"var server = app.listen( process.env.PORT || 3000, function(){\n  console.log('Listening on port ' + server.address().port);\n});\n",[5338],{"type":22,"tag":486,"props":5339,"children":5340},{"__ignoreMap":8},[5341,5401,5443],{"type":22,"tag":896,"props":5342,"children":5343},{"class":898,"line":899},[5344,5348,5353,5357,5362,5367,5372,5377,5382,5387,5391,5396],{"type":22,"tag":896,"props":5345,"children":5346},{"style":1691},[5347],{"type":27,"value":4682},{"type":22,"tag":896,"props":5349,"children":5350},{"style":903},[5351],{"type":27,"value":5352}," server ",{"type":22,"tag":896,"props":5354,"children":5355},{"style":1691},[5356],{"type":27,"value":941},{"type":22,"tag":896,"props":5358,"children":5359},{"style":903},[5360],{"type":27,"value":5361}," app.",{"type":22,"tag":896,"props":5363,"children":5364},{"style":933},[5365],{"type":27,"value":5366},"listen",{"type":22,"tag":896,"props":5368,"children":5369},{"style":903},[5370],{"type":27,"value":5371},"( process.env.",{"type":22,"tag":896,"props":5373,"children":5374},{"style":1632},[5375],{"type":27,"value":5376},"PORT",{"type":22,"tag":896,"props":5378,"children":5379},{"style":1691},[5380],{"type":27,"value":5381}," ||",{"type":22,"tag":896,"props":5383,"children":5384},{"style":1632},[5385],{"type":27,"value":5386}," 3000",{"type":22,"tag":896,"props":5388,"children":5389},{"style":903},[5390],{"type":27,"value":236},{"type":22,"tag":896,"props":5392,"children":5393},{"style":1691},[5394],{"type":27,"value":5395},"function",{"type":22,"tag":896,"props":5397,"children":5398},{"style":903},[5399],{"type":27,"value":5400},"(){\n",{"type":22,"tag":896,"props":5402,"children":5403},{"class":898,"line":758},[5404,5409,5414,5418,5423,5428,5433,5438],{"type":22,"tag":896,"props":5405,"children":5406},{"style":903},[5407],{"type":27,"value":5408},"  console.",{"type":22,"tag":896,"props":5410,"children":5411},{"style":933},[5412],{"type":27,"value":5413},"log",{"type":22,"tag":896,"props":5415,"children":5416},{"style":903},[5417],{"type":27,"value":2888},{"type":22,"tag":896,"props":5419,"children":5420},{"style":944},[5421],{"type":27,"value":5422},"'Listening on port '",{"type":22,"tag":896,"props":5424,"children":5425},{"style":1691},[5426],{"type":27,"value":5427}," +",{"type":22,"tag":896,"props":5429,"children":5430},{"style":903},[5431],{"type":27,"value":5432}," server.",{"type":22,"tag":896,"props":5434,"children":5435},{"style":933},[5436],{"type":27,"value":5437},"address",{"type":22,"tag":896,"props":5439,"children":5440},{"style":903},[5441],{"type":27,"value":5442},"().port);\n",{"type":22,"tag":896,"props":5444,"children":5445},{"class":898,"line":755},[5446],{"type":22,"tag":896,"props":5447,"children":5448},{"style":903},[5449],{"type":27,"value":5450},"});\n",{"type":22,"tag":23,"props":5452,"children":5453},{},[5454,5456,5463,5465,5470],{"type":27,"value":5455},"Fastify works similarly, but it has a ",{"type":22,"tag":30,"props":5457,"children":5460},{"href":5458,"rel":5459},"https://www.fastify.io/docs/latest/Reference/Plugins/",[34],[5461],{"type":27,"value":5462},"plugin system",{"type":27,"value":5464}," which provides a flexible way to extend a Fastify server's capabilities. One property that plugins can enforce is ",{"type":22,"tag":267,"props":5466,"children":5467},{},[5468],{"type":27,"value":5469},"encapsulation",{"type":27,"value":5471},": ensuring that the capabilities added by a plugin can only be used within that plugin.",{"type":22,"tag":23,"props":5473,"children":5474},{},[5475],{"type":27,"value":5476},"In this first step where we intend to temporarily load an Express application within a Fastify application, encapsulation is a great way to ensure that none of the Fastify-based code can inadvertently use functionality added by the Express application, so it makes sense to create a Fastify plugin for it. For now the entirety of the application will exist in there, but we'll change that incrementally later.",{"type":22,"tag":23,"props":5478,"children":5479},{},[5480,5482,5487,5489,5495,5497,5503],{"type":27,"value":5481},"The Fastify version of ",{"type":22,"tag":486,"props":5483,"children":5485},{"className":5484},[],[5486],{"type":27,"value":4544},{"type":27,"value":5488}," is similar in structure, defining a ",{"type":22,"tag":486,"props":5490,"children":5492},{"className":5491},[],[5493],{"type":27,"value":5494},"build()",{"type":27,"value":5496}," function which sets up the database and loads plugins, and then running it and calling a ",{"type":22,"tag":486,"props":5498,"children":5500},{"className":5499},[],[5501],{"type":27,"value":5502},".listen()",{"type":27,"value":5504}," method to start the server:",{"type":22,"tag":886,"props":5506,"children":5508},{"className":2505,"code":5507,"language":2507,"meta":8,"style":8},"var isProduction = process.env.NODE_ENV === 'production';\n\nasync function build() {\n  const mongoose = require('mongoose');\n\n  if(isProduction){\n    mongoose.connect(process.env.MONGODB_URI);\n  } else {\n    mongoose.connect('mongodb://localhost/conduit');\n    mongoose.set('debug', true);\n  }\n\n  require('./models/User');\n  require('./models/Article');\n  require('./models/Comment');\n  require('./config/passport');\n\n  // Create global app object\n  const app = require('fastify')({\n    logger: true,\n  });\n  await app.register(require('@fastify/formbody'));\n  await app.register(require('./routes/legacy'), {isProduction});\n  return app;\n}\n\nbuild()\n  .then(app => app.listen({port: 3000}))\n  .then((address) => {\n    console.log('Listening on ' + address);\n  }).catch(console.log);\n",[5509],{"type":22,"tag":486,"props":5510,"children":5511},{"__ignoreMap":8},[5512,5547,5554,5577,5610,5617,5629,5653,5669,5692,5723,5731,5738,5758,5777,5796,5815,5822,5830,5864,5880,5888,5926,5963,5976,5983,5991,6005,6057,6092,6123],{"type":22,"tag":896,"props":5513,"children":5514},{"class":898,"line":899},[5515,5519,5523,5527,5531,5535,5539,5543],{"type":22,"tag":896,"props":5516,"children":5517},{"style":1691},[5518],{"type":27,"value":4682},{"type":22,"tag":896,"props":5520,"children":5521},{"style":903},[5522],{"type":27,"value":4835},{"type":22,"tag":896,"props":5524,"children":5525},{"style":1691},[5526],{"type":27,"value":941},{"type":22,"tag":896,"props":5528,"children":5529},{"style":903},[5530],{"type":27,"value":4844},{"type":22,"tag":896,"props":5532,"children":5533},{"style":1632},[5534],{"type":27,"value":4849},{"type":22,"tag":896,"props":5536,"children":5537},{"style":1691},[5538],{"type":27,"value":4854},{"type":22,"tag":896,"props":5540,"children":5541},{"style":944},[5542],{"type":27,"value":4859},{"type":22,"tag":896,"props":5544,"children":5545},{"style":903},[5546],{"type":27,"value":1649},{"type":22,"tag":896,"props":5548,"children":5549},{"class":898,"line":758},[5550],{"type":22,"tag":896,"props":5551,"children":5552},{"emptyLinePlaceholder":3481},[5553],{"type":27,"value":3484},{"type":22,"tag":896,"props":5555,"children":5556},{"class":898,"line":755},[5557,5562,5567,5572],{"type":22,"tag":896,"props":5558,"children":5559},{"style":1691},[5560],{"type":27,"value":5561},"async",{"type":22,"tag":896,"props":5563,"children":5564},{"style":1691},[5565],{"type":27,"value":5566}," function",{"type":22,"tag":896,"props":5568,"children":5569},{"style":933},[5570],{"type":27,"value":5571}," build",{"type":22,"tag":896,"props":5573,"children":5574},{"style":903},[5575],{"type":27,"value":5576},"() {\n",{"type":22,"tag":896,"props":5578,"children":5579},{"class":898,"line":983},[5580,5585,5590,5594,5598,5602,5606],{"type":22,"tag":896,"props":5581,"children":5582},{"style":1691},[5583],{"type":27,"value":5584},"  const",{"type":22,"tag":896,"props":5586,"children":5587},{"style":1632},[5588],{"type":27,"value":5589}," mongoose",{"type":22,"tag":896,"props":5591,"children":5592},{"style":1691},[5593],{"type":27,"value":3638},{"type":22,"tag":896,"props":5595,"children":5596},{"style":933},[5597],{"type":27,"value":4696},{"type":22,"tag":896,"props":5599,"children":5600},{"style":903},[5601],{"type":27,"value":2888},{"type":22,"tag":896,"props":5603,"children":5604},{"style":944},[5605],{"type":27,"value":4804},{"type":22,"tag":896,"props":5607,"children":5608},{"style":903},[5609],{"type":27,"value":3678},{"type":22,"tag":896,"props":5611,"children":5612},{"class":898,"line":1013},[5613],{"type":22,"tag":896,"props":5614,"children":5615},{"emptyLinePlaceholder":3481},[5616],{"type":27,"value":3484},{"type":22,"tag":896,"props":5618,"children":5619},{"class":898,"line":1030},[5620,5625],{"type":22,"tag":896,"props":5621,"children":5622},{"style":1691},[5623],{"type":27,"value":5624},"  if",{"type":22,"tag":896,"props":5626,"children":5627},{"style":903},[5628],{"type":27,"value":5079},{"type":22,"tag":896,"props":5630,"children":5631},{"class":898,"line":1068},[5632,5637,5641,5645,5649],{"type":22,"tag":896,"props":5633,"children":5634},{"style":903},[5635],{"type":27,"value":5636},"    mongoose.",{"type":22,"tag":896,"props":5638,"children":5639},{"style":933},[5640],{"type":27,"value":5092},{"type":22,"tag":896,"props":5642,"children":5643},{"style":903},[5644],{"type":27,"value":5097},{"type":22,"tag":896,"props":5646,"children":5647},{"style":1632},[5648],{"type":27,"value":5102},{"type":22,"tag":896,"props":5650,"children":5651},{"style":903},[5652],{"type":27,"value":3678},{"type":22,"tag":896,"props":5654,"children":5655},{"class":898,"line":1085},[5656,5661,5665],{"type":22,"tag":896,"props":5657,"children":5658},{"style":903},[5659],{"type":27,"value":5660},"  } ",{"type":22,"tag":896,"props":5662,"children":5663},{"style":1691},[5664],{"type":27,"value":5119},{"type":22,"tag":896,"props":5666,"children":5667},{"style":903},[5668],{"type":27,"value":1626},{"type":22,"tag":896,"props":5670,"children":5671},{"class":898,"line":1123},[5672,5676,5680,5684,5688],{"type":22,"tag":896,"props":5673,"children":5674},{"style":903},[5675],{"type":27,"value":5636},{"type":22,"tag":896,"props":5677,"children":5678},{"style":933},[5679],{"type":27,"value":5092},{"type":22,"tag":896,"props":5681,"children":5682},{"style":903},[5683],{"type":27,"value":2888},{"type":22,"tag":896,"props":5685,"children":5686},{"style":944},[5687],{"type":27,"value":5143},{"type":22,"tag":896,"props":5689,"children":5690},{"style":903},[5691],{"type":27,"value":3678},{"type":22,"tag":896,"props":5693,"children":5694},{"class":898,"line":1161},[5695,5699,5703,5707,5711,5715,5719],{"type":22,"tag":896,"props":5696,"children":5697},{"style":903},[5698],{"type":27,"value":5636},{"type":22,"tag":896,"props":5700,"children":5701},{"style":933},[5702],{"type":27,"value":5159},{"type":22,"tag":896,"props":5704,"children":5705},{"style":903},[5706],{"type":27,"value":2888},{"type":22,"tag":896,"props":5708,"children":5709},{"style":944},[5710],{"type":27,"value":5168},{"type":22,"tag":896,"props":5712,"children":5713},{"style":903},[5714],{"type":27,"value":236},{"type":22,"tag":896,"props":5716,"children":5717},{"style":1632},[5718],{"type":27,"value":5177},{"type":22,"tag":896,"props":5720,"children":5721},{"style":903},[5722],{"type":27,"value":3678},{"type":22,"tag":896,"props":5724,"children":5725},{"class":898,"line":2298},[5726],{"type":22,"tag":896,"props":5727,"children":5728},{"style":903},[5729],{"type":27,"value":5730},"  }\n",{"type":22,"tag":896,"props":5732,"children":5733},{"class":898,"line":3170},[5734],{"type":22,"tag":896,"props":5735,"children":5736},{"emptyLinePlaceholder":3481},[5737],{"type":27,"value":3484},{"type":22,"tag":896,"props":5739,"children":5740},{"class":898,"line":3187},[5741,5746,5750,5754],{"type":22,"tag":896,"props":5742,"children":5743},{"style":933},[5744],{"type":27,"value":5745},"  require",{"type":22,"tag":896,"props":5747,"children":5748},{"style":903},[5749],{"type":27,"value":2888},{"type":22,"tag":896,"props":5751,"children":5752},{"style":944},[5753],{"type":27,"value":5211},{"type":22,"tag":896,"props":5755,"children":5756},{"style":903},[5757],{"type":27,"value":3678},{"type":22,"tag":896,"props":5759,"children":5760},{"class":898,"line":3228},[5761,5765,5769,5773],{"type":22,"tag":896,"props":5762,"children":5763},{"style":933},[5764],{"type":27,"value":5745},{"type":22,"tag":896,"props":5766,"children":5767},{"style":903},[5768],{"type":27,"value":2888},{"type":22,"tag":896,"props":5770,"children":5771},{"style":944},[5772],{"type":27,"value":5231},{"type":22,"tag":896,"props":5774,"children":5775},{"style":903},[5776],{"type":27,"value":3678},{"type":22,"tag":896,"props":5778,"children":5779},{"class":898,"line":3245},[5780,5784,5788,5792],{"type":22,"tag":896,"props":5781,"children":5782},{"style":933},[5783],{"type":27,"value":5745},{"type":22,"tag":896,"props":5785,"children":5786},{"style":903},[5787],{"type":27,"value":2888},{"type":22,"tag":896,"props":5789,"children":5790},{"style":944},[5791],{"type":27,"value":5251},{"type":22,"tag":896,"props":5793,"children":5794},{"style":903},[5795],{"type":27,"value":3678},{"type":22,"tag":896,"props":5797,"children":5798},{"class":898,"line":3286},[5799,5803,5807,5811],{"type":22,"tag":896,"props":5800,"children":5801},{"style":933},[5802],{"type":27,"value":5745},{"type":22,"tag":896,"props":5804,"children":5805},{"style":903},[5806],{"type":27,"value":2888},{"type":22,"tag":896,"props":5808,"children":5809},{"style":944},[5810],{"type":27,"value":5271},{"type":22,"tag":896,"props":5812,"children":5813},{"style":903},[5814],{"type":27,"value":3678},{"type":22,"tag":896,"props":5816,"children":5817},{"class":898,"line":3303},[5818],{"type":22,"tag":896,"props":5819,"children":5820},{"emptyLinePlaceholder":3481},[5821],{"type":27,"value":3484},{"type":22,"tag":896,"props":5823,"children":5824},{"class":898,"line":3344},[5825],{"type":22,"tag":896,"props":5826,"children":5827},{"style":2070},[5828],{"type":27,"value":5829},"  // Create global app object\n",{"type":22,"tag":896,"props":5831,"children":5832},{"class":898,"line":3361},[5833,5837,5842,5846,5850,5854,5859],{"type":22,"tag":896,"props":5834,"children":5835},{"style":1691},[5836],{"type":27,"value":5584},{"type":22,"tag":896,"props":5838,"children":5839},{"style":1632},[5840],{"type":27,"value":5841}," app",{"type":22,"tag":896,"props":5843,"children":5844},{"style":1691},[5845],{"type":27,"value":3638},{"type":22,"tag":896,"props":5847,"children":5848},{"style":933},[5849],{"type":27,"value":4696},{"type":22,"tag":896,"props":5851,"children":5852},{"style":903},[5853],{"type":27,"value":2888},{"type":22,"tag":896,"props":5855,"children":5856},{"style":944},[5857],{"type":27,"value":5858},"'fastify'",{"type":22,"tag":896,"props":5860,"children":5861},{"style":903},[5862],{"type":27,"value":5863},")({\n",{"type":22,"tag":896,"props":5865,"children":5866},{"class":898,"line":3402},[5867,5872,5876],{"type":22,"tag":896,"props":5868,"children":5869},{"style":903},[5870],{"type":27,"value":5871},"    logger: ",{"type":22,"tag":896,"props":5873,"children":5874},{"style":1632},[5875],{"type":27,"value":5177},{"type":22,"tag":896,"props":5877,"children":5878},{"style":903},[5879],{"type":27,"value":3988},{"type":22,"tag":896,"props":5881,"children":5882},{"class":898,"line":3419},[5883],{"type":22,"tag":896,"props":5884,"children":5885},{"style":903},[5886],{"type":27,"value":5887},"  });\n",{"type":22,"tag":896,"props":5889,"children":5890},{"class":898,"line":3460},[5891,5896,5900,5905,5909,5913,5917,5922],{"type":22,"tag":896,"props":5892,"children":5893},{"style":1691},[5894],{"type":27,"value":5895},"  await",{"type":22,"tag":896,"props":5897,"children":5898},{"style":903},[5899],{"type":27,"value":5361},{"type":22,"tag":896,"props":5901,"children":5902},{"style":933},[5903],{"type":27,"value":5904},"register",{"type":22,"tag":896,"props":5906,"children":5907},{"style":903},[5908],{"type":27,"value":2888},{"type":22,"tag":896,"props":5910,"children":5911},{"style":933},[5912],{"type":27,"value":4962},{"type":22,"tag":896,"props":5914,"children":5915},{"style":903},[5916],{"type":27,"value":2888},{"type":22,"tag":896,"props":5918,"children":5919},{"style":944},[5920],{"type":27,"value":5921},"'@fastify/formbody'",{"type":22,"tag":896,"props":5923,"children":5924},{"style":903},[5925],{"type":27,"value":4986},{"type":22,"tag":896,"props":5927,"children":5928},{"class":898,"line":3477},[5929,5933,5937,5941,5945,5949,5953,5958],{"type":22,"tag":896,"props":5930,"children":5931},{"style":1691},[5932],{"type":27,"value":5895},{"type":22,"tag":896,"props":5934,"children":5935},{"style":903},[5936],{"type":27,"value":5361},{"type":22,"tag":896,"props":5938,"children":5939},{"style":933},[5940],{"type":27,"value":5904},{"type":22,"tag":896,"props":5942,"children":5943},{"style":903},[5944],{"type":27,"value":2888},{"type":22,"tag":896,"props":5946,"children":5947},{"style":933},[5948],{"type":27,"value":4962},{"type":22,"tag":896,"props":5950,"children":5951},{"style":903},[5952],{"type":27,"value":2888},{"type":22,"tag":896,"props":5954,"children":5955},{"style":944},[5956],{"type":27,"value":5957},"'./routes/legacy'",{"type":22,"tag":896,"props":5959,"children":5960},{"style":903},[5961],{"type":27,"value":5962},"), {isProduction});\n",{"type":22,"tag":896,"props":5964,"children":5965},{"class":898,"line":3487},[5966,5971],{"type":22,"tag":896,"props":5967,"children":5968},{"style":1691},[5969],{"type":27,"value":5970},"  return",{"type":22,"tag":896,"props":5972,"children":5973},{"style":903},[5974],{"type":27,"value":5975}," app;\n",{"type":22,"tag":896,"props":5977,"children":5978},{"class":898,"line":3505},[5979],{"type":22,"tag":896,"props":5980,"children":5981},{"style":903},[5982],{"type":27,"value":1745},{"type":22,"tag":896,"props":5984,"children":5986},{"class":898,"line":5985},26,[5987],{"type":22,"tag":896,"props":5988,"children":5989},{"emptyLinePlaceholder":3481},[5990],{"type":27,"value":3484},{"type":22,"tag":896,"props":5992,"children":5994},{"class":898,"line":5993},27,[5995,6000],{"type":22,"tag":896,"props":5996,"children":5997},{"style":933},[5998],{"type":27,"value":5999},"build",{"type":22,"tag":896,"props":6001,"children":6002},{"style":903},[6003],{"type":27,"value":6004},"()\n",{"type":22,"tag":896,"props":6006,"children":6008},{"class":898,"line":6007},28,[6009,6014,6019,6023,6029,6034,6038,6042,6047,6052],{"type":22,"tag":896,"props":6010,"children":6011},{"style":903},[6012],{"type":27,"value":6013},"  .",{"type":22,"tag":896,"props":6015,"children":6016},{"style":933},[6017],{"type":27,"value":6018},"then",{"type":22,"tag":896,"props":6020,"children":6021},{"style":903},[6022],{"type":27,"value":2888},{"type":22,"tag":896,"props":6024,"children":6026},{"style":6025},"--shiki-default:#E36209;--shiki-dark:#FFAB70",[6027],{"type":27,"value":6028},"app",{"type":22,"tag":896,"props":6030,"children":6031},{"style":1691},[6032],{"type":27,"value":6033}," =>",{"type":22,"tag":896,"props":6035,"children":6036},{"style":903},[6037],{"type":27,"value":5361},{"type":22,"tag":896,"props":6039,"children":6040},{"style":933},[6041],{"type":27,"value":5366},{"type":22,"tag":896,"props":6043,"children":6044},{"style":903},[6045],{"type":27,"value":6046},"({port: ",{"type":22,"tag":896,"props":6048,"children":6049},{"style":1632},[6050],{"type":27,"value":6051},"3000",{"type":22,"tag":896,"props":6053,"children":6054},{"style":903},[6055],{"type":27,"value":6056},"}))\n",{"type":22,"tag":896,"props":6058,"children":6060},{"class":898,"line":6059},29,[6061,6065,6069,6074,6078,6083,6088],{"type":22,"tag":896,"props":6062,"children":6063},{"style":903},[6064],{"type":27,"value":6013},{"type":22,"tag":896,"props":6066,"children":6067},{"style":933},[6068],{"type":27,"value":6018},{"type":22,"tag":896,"props":6070,"children":6071},{"style":903},[6072],{"type":27,"value":6073},"((",{"type":22,"tag":896,"props":6075,"children":6076},{"style":6025},[6077],{"type":27,"value":5437},{"type":22,"tag":896,"props":6079,"children":6080},{"style":903},[6081],{"type":27,"value":6082},") ",{"type":22,"tag":896,"props":6084,"children":6085},{"style":1691},[6086],{"type":27,"value":6087},"=>",{"type":22,"tag":896,"props":6089,"children":6090},{"style":903},[6091],{"type":27,"value":1626},{"type":22,"tag":896,"props":6093,"children":6095},{"class":898,"line":6094},30,[6096,6101,6105,6109,6114,6118],{"type":22,"tag":896,"props":6097,"children":6098},{"style":903},[6099],{"type":27,"value":6100},"    console.",{"type":22,"tag":896,"props":6102,"children":6103},{"style":933},[6104],{"type":27,"value":5413},{"type":22,"tag":896,"props":6106,"children":6107},{"style":903},[6108],{"type":27,"value":2888},{"type":22,"tag":896,"props":6110,"children":6111},{"style":944},[6112],{"type":27,"value":6113},"'Listening on '",{"type":22,"tag":896,"props":6115,"children":6116},{"style":1691},[6117],{"type":27,"value":5427},{"type":22,"tag":896,"props":6119,"children":6120},{"style":903},[6121],{"type":27,"value":6122}," address);\n",{"type":22,"tag":896,"props":6124,"children":6126},{"class":898,"line":6125},31,[6127,6132,6137],{"type":22,"tag":896,"props":6128,"children":6129},{"style":903},[6130],{"type":27,"value":6131},"  }).",{"type":22,"tag":896,"props":6133,"children":6134},{"style":933},[6135],{"type":27,"value":6136},"catch",{"type":22,"tag":896,"props":6138,"children":6139},{"style":903},[6140],{"type":27,"value":6141},"(console.log);\n",{"type":22,"tag":23,"props":6143,"children":6144},{},[6145,6147,6153],{"type":27,"value":6146},"The Express application configuration has been moved to a plugin defined in ",{"type":22,"tag":486,"props":6148,"children":6150},{"className":6149},[],[6151],{"type":27,"value":6152},"routes/legacy/index.js",{"type":27,"value":6154},", which is registered in the Fastify app above. It looks like this:",{"type":22,"tag":886,"props":6156,"children":6158},{"className":2505,"code":6157,"language":2507,"meta":8,"style":8},"const express = require('express'),\n      session = require('express-session'),\n      fp = require('fastify-plugin'),\n      cors = require('cors'),\n      errorhandler = require('errorhandler');\n\nmodule.exports = fp(async function (fastify, opts) {\n  await fastify.register(require('@fastify/express'), {\n    // run express after `fastify-formbody` logic\n    expressHook: 'preHandler',\n  });\n\n  fastify.use(cors());\n\n  fastify.use(require('morgan')('dev'));\n\n  /* ... (the same .use() calls that used to be in app.js) */\n\n  const router = require('express').Router();\n  router.use('/api', require('./api'));\n\n  fastify.use(router);\n});\n",[6159],{"type":22,"tag":486,"props":6160,"children":6161},{"__ignoreMap":8},[6162,6195,6224,6253,6281,6310,6317,6378,6416,6424,6441,6448,6455,6479,6486,6525,6532,6540,6547,6588,6630,6637,6653],{"type":22,"tag":896,"props":6163,"children":6164},{"class":898,"line":899},[6165,6170,6174,6178,6182,6186,6190],{"type":22,"tag":896,"props":6166,"children":6167},{"style":1691},[6168],{"type":27,"value":6169},"const",{"type":22,"tag":896,"props":6171,"children":6172},{"style":1632},[6173],{"type":27,"value":4891},{"type":22,"tag":896,"props":6175,"children":6176},{"style":1691},[6177],{"type":27,"value":3638},{"type":22,"tag":896,"props":6179,"children":6180},{"style":933},[6181],{"type":27,"value":4696},{"type":22,"tag":896,"props":6183,"children":6184},{"style":903},[6185],{"type":27,"value":2888},{"type":22,"tag":896,"props":6187,"children":6188},{"style":944},[6189],{"type":27,"value":4705},{"type":22,"tag":896,"props":6191,"children":6192},{"style":903},[6193],{"type":27,"value":6194},"),\n",{"type":22,"tag":896,"props":6196,"children":6197},{"class":898,"line":758},[6198,6203,6207,6211,6215,6220],{"type":22,"tag":896,"props":6199,"children":6200},{"style":1632},[6201],{"type":27,"value":6202},"      session",{"type":22,"tag":896,"props":6204,"children":6205},{"style":1691},[6206],{"type":27,"value":3638},{"type":22,"tag":896,"props":6208,"children":6209},{"style":933},[6210],{"type":27,"value":4696},{"type":22,"tag":896,"props":6212,"children":6213},{"style":903},[6214],{"type":27,"value":2888},{"type":22,"tag":896,"props":6216,"children":6217},{"style":944},[6218],{"type":27,"value":6219},"'express-session'",{"type":22,"tag":896,"props":6221,"children":6222},{"style":903},[6223],{"type":27,"value":6194},{"type":22,"tag":896,"props":6225,"children":6226},{"class":898,"line":755},[6227,6232,6236,6240,6244,6249],{"type":22,"tag":896,"props":6228,"children":6229},{"style":1632},[6230],{"type":27,"value":6231},"      fp",{"type":22,"tag":896,"props":6233,"children":6234},{"style":1691},[6235],{"type":27,"value":3638},{"type":22,"tag":896,"props":6237,"children":6238},{"style":933},[6239],{"type":27,"value":4696},{"type":22,"tag":896,"props":6241,"children":6242},{"style":903},[6243],{"type":27,"value":2888},{"type":22,"tag":896,"props":6245,"children":6246},{"style":944},[6247],{"type":27,"value":6248},"'fastify-plugin'",{"type":22,"tag":896,"props":6250,"children":6251},{"style":903},[6252],{"type":27,"value":6194},{"type":22,"tag":896,"props":6254,"children":6255},{"class":898,"line":983},[6256,6261,6265,6269,6273,6277],{"type":22,"tag":896,"props":6257,"children":6258},{"style":1632},[6259],{"type":27,"value":6260},"      cors",{"type":22,"tag":896,"props":6262,"children":6263},{"style":1691},[6264],{"type":27,"value":3638},{"type":22,"tag":896,"props":6266,"children":6267},{"style":933},[6268],{"type":27,"value":4696},{"type":22,"tag":896,"props":6270,"children":6271},{"style":903},[6272],{"type":27,"value":2888},{"type":22,"tag":896,"props":6274,"children":6275},{"style":944},[6276],{"type":27,"value":4771},{"type":22,"tag":896,"props":6278,"children":6279},{"style":903},[6280],{"type":27,"value":6194},{"type":22,"tag":896,"props":6282,"children":6283},{"class":898,"line":1013},[6284,6289,6293,6297,6301,6306],{"type":22,"tag":896,"props":6285,"children":6286},{"style":1632},[6287],{"type":27,"value":6288},"      errorhandler",{"type":22,"tag":896,"props":6290,"children":6291},{"style":1691},[6292],{"type":27,"value":3638},{"type":22,"tag":896,"props":6294,"children":6295},{"style":933},[6296],{"type":27,"value":4696},{"type":22,"tag":896,"props":6298,"children":6299},{"style":903},[6300],{"type":27,"value":2888},{"type":22,"tag":896,"props":6302,"children":6303},{"style":944},[6304],{"type":27,"value":6305},"'errorhandler'",{"type":22,"tag":896,"props":6307,"children":6308},{"style":903},[6309],{"type":27,"value":3678},{"type":22,"tag":896,"props":6311,"children":6312},{"class":898,"line":1030},[6313],{"type":22,"tag":896,"props":6314,"children":6315},{"emptyLinePlaceholder":3481},[6316],{"type":27,"value":3484},{"type":22,"tag":896,"props":6318,"children":6319},{"class":898,"line":1068},[6320,6325,6329,6334,6338,6343,6347,6351,6355,6359,6364,6368,6373],{"type":22,"tag":896,"props":6321,"children":6322},{"style":1632},[6323],{"type":27,"value":6324},"module",{"type":22,"tag":896,"props":6326,"children":6327},{"style":903},[6328],{"type":27,"value":71},{"type":22,"tag":896,"props":6330,"children":6331},{"style":1632},[6332],{"type":27,"value":6333},"exports",{"type":22,"tag":896,"props":6335,"children":6336},{"style":1691},[6337],{"type":27,"value":3638},{"type":22,"tag":896,"props":6339,"children":6340},{"style":933},[6341],{"type":27,"value":6342}," fp",{"type":22,"tag":896,"props":6344,"children":6345},{"style":903},[6346],{"type":27,"value":2888},{"type":22,"tag":896,"props":6348,"children":6349},{"style":1691},[6350],{"type":27,"value":5561},{"type":22,"tag":896,"props":6352,"children":6353},{"style":1691},[6354],{"type":27,"value":5566},{"type":22,"tag":896,"props":6356,"children":6357},{"style":903},[6358],{"type":27,"value":3643},{"type":22,"tag":896,"props":6360,"children":6361},{"style":6025},[6362],{"type":27,"value":6363},"fastify",{"type":22,"tag":896,"props":6365,"children":6366},{"style":903},[6367],{"type":27,"value":236},{"type":22,"tag":896,"props":6369,"children":6370},{"style":6025},[6371],{"type":27,"value":6372},"opts",{"type":22,"tag":896,"props":6374,"children":6375},{"style":903},[6376],{"type":27,"value":6377},") {\n",{"type":22,"tag":896,"props":6379,"children":6380},{"class":898,"line":1085},[6381,6385,6390,6394,6398,6402,6406,6411],{"type":22,"tag":896,"props":6382,"children":6383},{"style":1691},[6384],{"type":27,"value":5895},{"type":22,"tag":896,"props":6386,"children":6387},{"style":903},[6388],{"type":27,"value":6389}," fastify.",{"type":22,"tag":896,"props":6391,"children":6392},{"style":933},[6393],{"type":27,"value":5904},{"type":22,"tag":896,"props":6395,"children":6396},{"style":903},[6397],{"type":27,"value":2888},{"type":22,"tag":896,"props":6399,"children":6400},{"style":933},[6401],{"type":27,"value":4962},{"type":22,"tag":896,"props":6403,"children":6404},{"style":903},[6405],{"type":27,"value":2888},{"type":22,"tag":896,"props":6407,"children":6408},{"style":944},[6409],{"type":27,"value":6410},"'@fastify/express'",{"type":22,"tag":896,"props":6412,"children":6413},{"style":903},[6414],{"type":27,"value":6415},"), {\n",{"type":22,"tag":896,"props":6417,"children":6418},{"class":898,"line":1123},[6419],{"type":22,"tag":896,"props":6420,"children":6421},{"style":2070},[6422],{"type":27,"value":6423},"    // run express after `fastify-formbody` logic\n",{"type":22,"tag":896,"props":6425,"children":6426},{"class":898,"line":1161},[6427,6432,6437],{"type":22,"tag":896,"props":6428,"children":6429},{"style":903},[6430],{"type":27,"value":6431},"    expressHook: ",{"type":22,"tag":896,"props":6433,"children":6434},{"style":944},[6435],{"type":27,"value":6436},"'preHandler'",{"type":22,"tag":896,"props":6438,"children":6439},{"style":903},[6440],{"type":27,"value":3988},{"type":22,"tag":896,"props":6442,"children":6443},{"class":898,"line":2298},[6444],{"type":22,"tag":896,"props":6445,"children":6446},{"style":903},[6447],{"type":27,"value":5887},{"type":22,"tag":896,"props":6449,"children":6450},{"class":898,"line":3170},[6451],{"type":22,"tag":896,"props":6452,"children":6453},{"emptyLinePlaceholder":3481},[6454],{"type":27,"value":3484},{"type":22,"tag":896,"props":6456,"children":6457},{"class":898,"line":3187},[6458,6463,6467,6471,6475],{"type":22,"tag":896,"props":6459,"children":6460},{"style":903},[6461],{"type":27,"value":6462},"  fastify.",{"type":22,"tag":896,"props":6464,"children":6465},{"style":933},[6466],{"type":27,"value":4921},{"type":22,"tag":896,"props":6468,"children":6469},{"style":903},[6470],{"type":27,"value":2888},{"type":22,"tag":896,"props":6472,"children":6473},{"style":933},[6474],{"type":27,"value":4930},{"type":22,"tag":896,"props":6476,"children":6477},{"style":903},[6478],{"type":27,"value":4935},{"type":22,"tag":896,"props":6480,"children":6481},{"class":898,"line":3228},[6482],{"type":22,"tag":896,"props":6483,"children":6484},{"emptyLinePlaceholder":3481},[6485],{"type":27,"value":3484},{"type":22,"tag":896,"props":6487,"children":6488},{"class":898,"line":3245},[6489,6493,6497,6501,6505,6509,6513,6517,6521],{"type":22,"tag":896,"props":6490,"children":6491},{"style":903},[6492],{"type":27,"value":6462},{"type":22,"tag":896,"props":6494,"children":6495},{"style":933},[6496],{"type":27,"value":4921},{"type":22,"tag":896,"props":6498,"children":6499},{"style":903},[6500],{"type":27,"value":2888},{"type":22,"tag":896,"props":6502,"children":6503},{"style":933},[6504],{"type":27,"value":4962},{"type":22,"tag":896,"props":6506,"children":6507},{"style":903},[6508],{"type":27,"value":2888},{"type":22,"tag":896,"props":6510,"children":6511},{"style":944},[6512],{"type":27,"value":4971},{"type":22,"tag":896,"props":6514,"children":6515},{"style":903},[6516],{"type":27,"value":4976},{"type":22,"tag":896,"props":6518,"children":6519},{"style":944},[6520],{"type":27,"value":4981},{"type":22,"tag":896,"props":6522,"children":6523},{"style":903},[6524],{"type":27,"value":4986},{"type":22,"tag":896,"props":6526,"children":6527},{"class":898,"line":3286},[6528],{"type":22,"tag":896,"props":6529,"children":6530},{"emptyLinePlaceholder":3481},[6531],{"type":27,"value":3484},{"type":22,"tag":896,"props":6533,"children":6534},{"class":898,"line":3303},[6535],{"type":22,"tag":896,"props":6536,"children":6537},{"style":2070},[6538],{"type":27,"value":6539},"  /* ... (the same .use() calls that used to be in app.js) */\n",{"type":22,"tag":896,"props":6541,"children":6542},{"class":898,"line":3344},[6543],{"type":22,"tag":896,"props":6544,"children":6545},{"emptyLinePlaceholder":3481},[6546],{"type":27,"value":3484},{"type":22,"tag":896,"props":6548,"children":6549},{"class":898,"line":3361},[6550,6554,6559,6563,6567,6571,6575,6579,6584],{"type":22,"tag":896,"props":6551,"children":6552},{"style":1691},[6553],{"type":27,"value":5584},{"type":22,"tag":896,"props":6555,"children":6556},{"style":1632},[6557],{"type":27,"value":6558}," router",{"type":22,"tag":896,"props":6560,"children":6561},{"style":1691},[6562],{"type":27,"value":3638},{"type":22,"tag":896,"props":6564,"children":6565},{"style":933},[6566],{"type":27,"value":4696},{"type":22,"tag":896,"props":6568,"children":6569},{"style":903},[6570],{"type":27,"value":2888},{"type":22,"tag":896,"props":6572,"children":6573},{"style":944},[6574],{"type":27,"value":4705},{"type":22,"tag":896,"props":6576,"children":6577},{"style":903},[6578],{"type":27,"value":290},{"type":22,"tag":896,"props":6580,"children":6581},{"style":933},[6582],{"type":27,"value":6583},"Router",{"type":22,"tag":896,"props":6585,"children":6586},{"style":903},[6587],{"type":27,"value":4896},{"type":22,"tag":896,"props":6589,"children":6590},{"class":898,"line":3402},[6591,6596,6600,6604,6609,6613,6617,6621,6626],{"type":22,"tag":896,"props":6592,"children":6593},{"style":903},[6594],{"type":27,"value":6595},"  router.",{"type":22,"tag":896,"props":6597,"children":6598},{"style":933},[6599],{"type":27,"value":4921},{"type":22,"tag":896,"props":6601,"children":6602},{"style":903},[6603],{"type":27,"value":2888},{"type":22,"tag":896,"props":6605,"children":6606},{"style":944},[6607],{"type":27,"value":6608},"'/api'",{"type":22,"tag":896,"props":6610,"children":6611},{"style":903},[6612],{"type":27,"value":236},{"type":22,"tag":896,"props":6614,"children":6615},{"style":933},[6616],{"type":27,"value":4962},{"type":22,"tag":896,"props":6618,"children":6619},{"style":903},[6620],{"type":27,"value":2888},{"type":22,"tag":896,"props":6622,"children":6623},{"style":944},[6624],{"type":27,"value":6625},"'./api'",{"type":22,"tag":896,"props":6627,"children":6628},{"style":903},[6629],{"type":27,"value":4986},{"type":22,"tag":896,"props":6631,"children":6632},{"class":898,"line":3419},[6633],{"type":22,"tag":896,"props":6634,"children":6635},{"emptyLinePlaceholder":3481},[6636],{"type":27,"value":3484},{"type":22,"tag":896,"props":6638,"children":6639},{"class":898,"line":3460},[6640,6644,6648],{"type":22,"tag":896,"props":6641,"children":6642},{"style":903},[6643],{"type":27,"value":6462},{"type":22,"tag":896,"props":6645,"children":6646},{"style":933},[6647],{"type":27,"value":4921},{"type":22,"tag":896,"props":6649,"children":6650},{"style":903},[6651],{"type":27,"value":6652},"(router);\n",{"type":22,"tag":896,"props":6654,"children":6655},{"class":898,"line":3477},[6656],{"type":22,"tag":896,"props":6657,"children":6658},{"style":903},[6659],{"type":27,"value":5450},{"type":22,"tag":23,"props":6661,"children":6662},{},[6663,6665,6670,6672,6677,6679,6684],{"type":27,"value":6664},"For the most part it contains the same Express configuration that previously existed in ",{"type":22,"tag":486,"props":6666,"children":6668},{"className":6667},[],[6669],{"type":27,"value":4544},{"type":27,"value":6671},", except that it's contained in a Fastify plugin, and the configuration is applied to the ",{"type":22,"tag":486,"props":6673,"children":6675},{"className":6674},[],[6676],{"type":27,"value":6363},{"type":27,"value":6678}," instance provided to the plugin. The plugin registers ",{"type":22,"tag":486,"props":6680,"children":6682},{"className":6681},[],[6683],{"type":27,"value":4393},{"type":27,"value":6685}," as a sub-plugin, which is what makes all that Express configuration work seamlessly.",{"type":22,"tag":23,"props":6687,"children":6688},{},[6689,6691,6696,6698,6704,6706,6712,6714,6719,6721,6726],{"type":27,"value":6690},"There's one messy bit, which is the business with ",{"type":22,"tag":486,"props":6692,"children":6694},{"className":6693},[],[6695],{"type":27,"value":4605},{"type":27,"value":6697}," and the ",{"type":22,"tag":486,"props":6699,"children":6701},{"className":6700},[],[6702],{"type":27,"value":6703},"expressHook: 'preHandler'",{"type":27,"value":6705}," configuration line. There's a known incompatibility with the Express ",{"type":22,"tag":486,"props":6707,"children":6709},{"className":6708},[],[6710],{"type":27,"value":6711},"body-parser",{"type":27,"value":6713}," library, and the workaround is to immediately remove ",{"type":22,"tag":486,"props":6715,"children":6717},{"className":6716},[],[6718],{"type":27,"value":6711},{"type":27,"value":6720}," and substitute it with the ",{"type":22,"tag":486,"props":6722,"children":6724},{"className":6723},[],[6725],{"type":27,"value":4605},{"type":27,"value":6727}," plugin which works similarly. This was the only hangup like this that I encountered; otherwise the application runs just like before, using Fastify as the server.",{"type":22,"tag":23,"props":6729,"children":6730},{},[6731,6733,6739],{"type":27,"value":6732},"Click ",{"type":22,"tag":30,"props":6734,"children":6737},{"href":6735,"rel":6736},"https://github.com/artandlogic/node-express-to-fastify-example/commit/38504ce47943246eb71aa25894ecae00cc302955",[34],[6738],{"type":27,"value":4369},{"type":27,"value":6740}," to view the full set of changes up to this point.",{"type":22,"tag":193,"props":6742,"children":6744},{"id":6743},"step-2-migrating-a-route-to-fastify",[6745],{"type":27,"value":6746},"Step 2: Migrating a route to Fastify",{"type":22,"tag":1481,"props":6748,"children":6750},{"id":6749},"prerequisite-1-implementing-authentication-decorators-for-fastify-routes",[6751],{"type":27,"value":6752},"Prerequisite #1: Implementing authentication decorators for Fastify routes",{"type":22,"tag":23,"props":6754,"children":6755},{},[6756,6758,6764,6765,6771],{"type":27,"value":6757},"In the Express application, authentication is applied to a route by adding an ",{"type":22,"tag":486,"props":6759,"children":6761},{"className":6760},[],[6762],{"type":27,"value":6763},"auth.optional",{"type":27,"value":2684},{"type":22,"tag":486,"props":6766,"children":6768},{"className":6767},[],[6769],{"type":27,"value":6770},"auth.required",{"type":27,"value":6772}," middleware. Before migrating a route to Fastify, we'll need an equivalent way to add authentication requirements to a route.",{"type":22,"tag":23,"props":6774,"children":6775},{},[6776,6778,6784,6785,6792,6794,6800],{"type":27,"value":6777},"In Fastify, this can be accomplished via ",{"type":22,"tag":30,"props":6779,"children":6782},{"href":6780,"rel":6781},"https://www.fastify.io/docs/latest/Reference/Hooks/",[34],[6783],{"type":27,"value":225},{"type":27,"value":1441},{"type":22,"tag":30,"props":6786,"children":6789},{"href":6787,"rel":6788},"https://www.fastify.io/docs/latest/Reference/Decorators/",[34],[6790],{"type":27,"value":6791},"Decorators",{"type":27,"value":6793},". For organizational purposes, this can be set up as a Fastify plugin, ",{"type":22,"tag":486,"props":6795,"children":6797},{"className":6796},[],[6798],{"type":27,"value":6799},"plugins/auth.js",{"type":27,"value":6801},":",{"type":22,"tag":886,"props":6803,"children":6806},{"className":6804,"code":6805,"language":27},[4580],"npm install --save @fastify/jwt\n",[6807],{"type":22,"tag":486,"props":6808,"children":6809},{"__ignoreMap":8},[6810],{"type":27,"value":6805},{"type":22,"tag":886,"props":6812,"children":6814},{"className":2505,"code":6813,"language":2507,"meta":8,"style":8},"const fp = require(\"fastify-plugin\");\nconst secret = require('../config').secret;\n\nmodule.exports = fp(async function (fastify, opts) {\n  await fastify.register(require(\"@fastify/jwt\"), {\n    // @fastify/jwt puts the token data on request.user by default; use\n    // request.payload to match the existing application\n    decoratorName: 'payload',\n    secret,\n    verify: {\n      // The application uses \"Token\" in the Authorization header rather than\n      // the more standard \"Bearer\", so add custom code to parse that\n      extractToken: (request) => {\n        const parts = request.headers.authorization.split(' ');\n        if (parts.length !== 2) {\n          throw new BadRequestError();\n        }\n\n        const [scheme, token] = parts;\n\n        if (!/^Token$/i.test(scheme)) {\n          throw new BadRequestError()\n        }\n\n        return token;\n      },\n    },\n  });\n  // Add a decorator so that routes can reference this via `fastify.authenticated`\n  await fastify.decorate(\"authenticated\", async function(request, reply) {\n    try {\n      await request.jwtVerify();\n    } catch (err) {\n      reply.send(err);\n    }\n  });\n  // Add a decorator so that routes can reference this via `fastify.authenticatedOptional`\n  await fastify.decorate(\"authenticatedOptional\", async function(request, reply) {\n    try {\n      await request.jwtVerify();\n    } catch {}\n  });\n});\n",[6815],{"type":22,"tag":486,"props":6816,"children":6817},{"__ignoreMap":8},[6818,6850,6884,6891,6946,6982,6990,6998,7015,7023,7031,7039,7047,7077,7117,7149,7170,7178,7185,7225,7232,7292,7311,7318,7325,7337,7345,7353,7360,7368,7426,7438,7461,7479,7498,7507,7515,7524,7581,7593,7613,7630,7638],{"type":22,"tag":896,"props":6819,"children":6820},{"class":898,"line":899},[6821,6825,6829,6833,6837,6841,6846],{"type":22,"tag":896,"props":6822,"children":6823},{"style":1691},[6824],{"type":27,"value":6169},{"type":22,"tag":896,"props":6826,"children":6827},{"style":1632},[6828],{"type":27,"value":6342},{"type":22,"tag":896,"props":6830,"children":6831},{"style":1691},[6832],{"type":27,"value":3638},{"type":22,"tag":896,"props":6834,"children":6835},{"style":933},[6836],{"type":27,"value":4696},{"type":22,"tag":896,"props":6838,"children":6839},{"style":903},[6840],{"type":27,"value":2888},{"type":22,"tag":896,"props":6842,"children":6843},{"style":944},[6844],{"type":27,"value":6845},"\"fastify-plugin\"",{"type":22,"tag":896,"props":6847,"children":6848},{"style":903},[6849],{"type":27,"value":3678},{"type":22,"tag":896,"props":6851,"children":6852},{"class":898,"line":758},[6853,6857,6862,6866,6870,6874,6879],{"type":22,"tag":896,"props":6854,"children":6855},{"style":1691},[6856],{"type":27,"value":6169},{"type":22,"tag":896,"props":6858,"children":6859},{"style":1632},[6860],{"type":27,"value":6861}," secret",{"type":22,"tag":896,"props":6863,"children":6864},{"style":1691},[6865],{"type":27,"value":3638},{"type":22,"tag":896,"props":6867,"children":6868},{"style":933},[6869],{"type":27,"value":4696},{"type":22,"tag":896,"props":6871,"children":6872},{"style":903},[6873],{"type":27,"value":2888},{"type":22,"tag":896,"props":6875,"children":6876},{"style":944},[6877],{"type":27,"value":6878},"'../config'",{"type":22,"tag":896,"props":6880,"children":6881},{"style":903},[6882],{"type":27,"value":6883},").secret;\n",{"type":22,"tag":896,"props":6885,"children":6886},{"class":898,"line":755},[6887],{"type":22,"tag":896,"props":6888,"children":6889},{"emptyLinePlaceholder":3481},[6890],{"type":27,"value":3484},{"type":22,"tag":896,"props":6892,"children":6893},{"class":898,"line":983},[6894,6898,6902,6906,6910,6914,6918,6922,6926,6930,6934,6938,6942],{"type":22,"tag":896,"props":6895,"children":6896},{"style":1632},[6897],{"type":27,"value":6324},{"type":22,"tag":896,"props":6899,"children":6900},{"style":903},[6901],{"type":27,"value":71},{"type":22,"tag":896,"props":6903,"children":6904},{"style":1632},[6905],{"type":27,"value":6333},{"type":22,"tag":896,"props":6907,"children":6908},{"style":1691},[6909],{"type":27,"value":3638},{"type":22,"tag":896,"props":6911,"children":6912},{"style":933},[6913],{"type":27,"value":6342},{"type":22,"tag":896,"props":6915,"children":6916},{"style":903},[6917],{"type":27,"value":2888},{"type":22,"tag":896,"props":6919,"children":6920},{"style":1691},[6921],{"type":27,"value":5561},{"type":22,"tag":896,"props":6923,"children":6924},{"style":1691},[6925],{"type":27,"value":5566},{"type":22,"tag":896,"props":6927,"children":6928},{"style":903},[6929],{"type":27,"value":3643},{"type":22,"tag":896,"props":6931,"children":6932},{"style":6025},[6933],{"type":27,"value":6363},{"type":22,"tag":896,"props":6935,"children":6936},{"style":903},[6937],{"type":27,"value":236},{"type":22,"tag":896,"props":6939,"children":6940},{"style":6025},[6941],{"type":27,"value":6372},{"type":22,"tag":896,"props":6943,"children":6944},{"style":903},[6945],{"type":27,"value":6377},{"type":22,"tag":896,"props":6947,"children":6948},{"class":898,"line":1013},[6949,6953,6957,6961,6965,6969,6973,6978],{"type":22,"tag":896,"props":6950,"children":6951},{"style":1691},[6952],{"type":27,"value":5895},{"type":22,"tag":896,"props":6954,"children":6955},{"style":903},[6956],{"type":27,"value":6389},{"type":22,"tag":896,"props":6958,"children":6959},{"style":933},[6960],{"type":27,"value":5904},{"type":22,"tag":896,"props":6962,"children":6963},{"style":903},[6964],{"type":27,"value":2888},{"type":22,"tag":896,"props":6966,"children":6967},{"style":933},[6968],{"type":27,"value":4962},{"type":22,"tag":896,"props":6970,"children":6971},{"style":903},[6972],{"type":27,"value":2888},{"type":22,"tag":896,"props":6974,"children":6975},{"style":944},[6976],{"type":27,"value":6977},"\"@fastify/jwt\"",{"type":22,"tag":896,"props":6979,"children":6980},{"style":903},[6981],{"type":27,"value":6415},{"type":22,"tag":896,"props":6983,"children":6984},{"class":898,"line":1030},[6985],{"type":22,"tag":896,"props":6986,"children":6987},{"style":2070},[6988],{"type":27,"value":6989},"    // @fastify/jwt puts the token data on request.user by default; use\n",{"type":22,"tag":896,"props":6991,"children":6992},{"class":898,"line":1068},[6993],{"type":22,"tag":896,"props":6994,"children":6995},{"style":2070},[6996],{"type":27,"value":6997},"    // request.payload to match the existing application\n",{"type":22,"tag":896,"props":6999,"children":7000},{"class":898,"line":1085},[7001,7006,7011],{"type":22,"tag":896,"props":7002,"children":7003},{"style":903},[7004],{"type":27,"value":7005},"    decoratorName: ",{"type":22,"tag":896,"props":7007,"children":7008},{"style":944},[7009],{"type":27,"value":7010},"'payload'",{"type":22,"tag":896,"props":7012,"children":7013},{"style":903},[7014],{"type":27,"value":3988},{"type":22,"tag":896,"props":7016,"children":7017},{"class":898,"line":1123},[7018],{"type":22,"tag":896,"props":7019,"children":7020},{"style":903},[7021],{"type":27,"value":7022},"    secret,\n",{"type":22,"tag":896,"props":7024,"children":7025},{"class":898,"line":1161},[7026],{"type":22,"tag":896,"props":7027,"children":7028},{"style":903},[7029],{"type":27,"value":7030},"    verify: {\n",{"type":22,"tag":896,"props":7032,"children":7033},{"class":898,"line":2298},[7034],{"type":22,"tag":896,"props":7035,"children":7036},{"style":2070},[7037],{"type":27,"value":7038},"      // The application uses \"Token\" in the Authorization header rather than\n",{"type":22,"tag":896,"props":7040,"children":7041},{"class":898,"line":3170},[7042],{"type":22,"tag":896,"props":7043,"children":7044},{"style":2070},[7045],{"type":27,"value":7046},"      // the more standard \"Bearer\", so add custom code to parse that\n",{"type":22,"tag":896,"props":7048,"children":7049},{"class":898,"line":3187},[7050,7055,7060,7065,7069,7073],{"type":22,"tag":896,"props":7051,"children":7052},{"style":933},[7053],{"type":27,"value":7054},"      extractToken",{"type":22,"tag":896,"props":7056,"children":7057},{"style":903},[7058],{"type":27,"value":7059},": (",{"type":22,"tag":896,"props":7061,"children":7062},{"style":6025},[7063],{"type":27,"value":7064},"request",{"type":22,"tag":896,"props":7066,"children":7067},{"style":903},[7068],{"type":27,"value":6082},{"type":22,"tag":896,"props":7070,"children":7071},{"style":1691},[7072],{"type":27,"value":6087},{"type":22,"tag":896,"props":7074,"children":7075},{"style":903},[7076],{"type":27,"value":1626},{"type":22,"tag":896,"props":7078,"children":7079},{"class":898,"line":3228},[7080,7085,7090,7094,7099,7104,7108,7113],{"type":22,"tag":896,"props":7081,"children":7082},{"style":1691},[7083],{"type":27,"value":7084},"        const",{"type":22,"tag":896,"props":7086,"children":7087},{"style":1632},[7088],{"type":27,"value":7089}," parts",{"type":22,"tag":896,"props":7091,"children":7092},{"style":1691},[7093],{"type":27,"value":3638},{"type":22,"tag":896,"props":7095,"children":7096},{"style":903},[7097],{"type":27,"value":7098}," request.headers.authorization.",{"type":22,"tag":896,"props":7100,"children":7101},{"style":933},[7102],{"type":27,"value":7103},"split",{"type":22,"tag":896,"props":7105,"children":7106},{"style":903},[7107],{"type":27,"value":2888},{"type":22,"tag":896,"props":7109,"children":7110},{"style":944},[7111],{"type":27,"value":7112},"' '",{"type":22,"tag":896,"props":7114,"children":7115},{"style":903},[7116],{"type":27,"value":3678},{"type":22,"tag":896,"props":7118,"children":7119},{"class":898,"line":3245},[7120,7125,7130,7135,7140,7145],{"type":22,"tag":896,"props":7121,"children":7122},{"style":1691},[7123],{"type":27,"value":7124},"        if",{"type":22,"tag":896,"props":7126,"children":7127},{"style":903},[7128],{"type":27,"value":7129}," (parts.",{"type":22,"tag":896,"props":7131,"children":7132},{"style":1632},[7133],{"type":27,"value":7134},"length",{"type":22,"tag":896,"props":7136,"children":7137},{"style":1691},[7138],{"type":27,"value":7139}," !==",{"type":22,"tag":896,"props":7141,"children":7142},{"style":1632},[7143],{"type":27,"value":7144}," 2",{"type":22,"tag":896,"props":7146,"children":7147},{"style":903},[7148],{"type":27,"value":6377},{"type":22,"tag":896,"props":7150,"children":7151},{"class":898,"line":3286},[7152,7157,7161,7166],{"type":22,"tag":896,"props":7153,"children":7154},{"style":1691},[7155],{"type":27,"value":7156},"          throw",{"type":22,"tag":896,"props":7158,"children":7159},{"style":1691},[7160],{"type":27,"value":3690},{"type":22,"tag":896,"props":7162,"children":7163},{"style":933},[7164],{"type":27,"value":7165}," BadRequestError",{"type":22,"tag":896,"props":7167,"children":7168},{"style":903},[7169],{"type":27,"value":4896},{"type":22,"tag":896,"props":7171,"children":7172},{"class":898,"line":3303},[7173],{"type":22,"tag":896,"props":7174,"children":7175},{"style":903},[7176],{"type":27,"value":7177},"        }\n",{"type":22,"tag":896,"props":7179,"children":7180},{"class":898,"line":3344},[7181],{"type":22,"tag":896,"props":7182,"children":7183},{"emptyLinePlaceholder":3481},[7184],{"type":27,"value":3484},{"type":22,"tag":896,"props":7186,"children":7187},{"class":898,"line":3361},[7188,7192,7197,7202,7206,7211,7216,7220],{"type":22,"tag":896,"props":7189,"children":7190},{"style":1691},[7191],{"type":27,"value":7084},{"type":22,"tag":896,"props":7193,"children":7194},{"style":903},[7195],{"type":27,"value":7196}," [",{"type":22,"tag":896,"props":7198,"children":7199},{"style":1632},[7200],{"type":27,"value":7201},"scheme",{"type":22,"tag":896,"props":7203,"children":7204},{"style":903},[7205],{"type":27,"value":236},{"type":22,"tag":896,"props":7207,"children":7208},{"style":1632},[7209],{"type":27,"value":7210},"token",{"type":22,"tag":896,"props":7212,"children":7213},{"style":903},[7214],{"type":27,"value":7215},"] ",{"type":22,"tag":896,"props":7217,"children":7218},{"style":1691},[7219],{"type":27,"value":941},{"type":22,"tag":896,"props":7221,"children":7222},{"style":903},[7223],{"type":27,"value":7224}," parts;\n",{"type":22,"tag":896,"props":7226,"children":7227},{"class":898,"line":3402},[7228],{"type":22,"tag":896,"props":7229,"children":7230},{"emptyLinePlaceholder":3481},[7231],{"type":27,"value":3484},{"type":22,"tag":896,"props":7233,"children":7234},{"class":898,"line":3419},[7235,7239,7243,7248,7253,7258,7264,7269,7273,7278,7282,7287],{"type":22,"tag":896,"props":7236,"children":7237},{"style":1691},[7238],{"type":27,"value":7124},{"type":22,"tag":896,"props":7240,"children":7241},{"style":903},[7242],{"type":27,"value":3643},{"type":22,"tag":896,"props":7244,"children":7245},{"style":1691},[7246],{"type":27,"value":7247},"!",{"type":22,"tag":896,"props":7249,"children":7250},{"style":944},[7251],{"type":27,"value":7252},"/",{"type":22,"tag":896,"props":7254,"children":7255},{"style":1691},[7256],{"type":27,"value":7257},"^",{"type":22,"tag":896,"props":7259,"children":7261},{"style":7260},"--shiki-default:#032F62;--shiki-dark:#DBEDFF",[7262],{"type":27,"value":7263},"Token",{"type":22,"tag":896,"props":7265,"children":7266},{"style":1691},[7267],{"type":27,"value":7268},"$",{"type":22,"tag":896,"props":7270,"children":7271},{"style":944},[7272],{"type":27,"value":7252},{"type":22,"tag":896,"props":7274,"children":7275},{"style":1691},[7276],{"type":27,"value":7277},"i",{"type":22,"tag":896,"props":7279,"children":7280},{"style":903},[7281],{"type":27,"value":71},{"type":22,"tag":896,"props":7283,"children":7284},{"style":933},[7285],{"type":27,"value":7286},"test",{"type":22,"tag":896,"props":7288,"children":7289},{"style":903},[7290],{"type":27,"value":7291},"(scheme)) {\n",{"type":22,"tag":896,"props":7293,"children":7294},{"class":898,"line":3460},[7295,7299,7303,7307],{"type":22,"tag":896,"props":7296,"children":7297},{"style":1691},[7298],{"type":27,"value":7156},{"type":22,"tag":896,"props":7300,"children":7301},{"style":1691},[7302],{"type":27,"value":3690},{"type":22,"tag":896,"props":7304,"children":7305},{"style":933},[7306],{"type":27,"value":7165},{"type":22,"tag":896,"props":7308,"children":7309},{"style":903},[7310],{"type":27,"value":6004},{"type":22,"tag":896,"props":7312,"children":7313},{"class":898,"line":3477},[7314],{"type":22,"tag":896,"props":7315,"children":7316},{"style":903},[7317],{"type":27,"value":7177},{"type":22,"tag":896,"props":7319,"children":7320},{"class":898,"line":3487},[7321],{"type":22,"tag":896,"props":7322,"children":7323},{"emptyLinePlaceholder":3481},[7324],{"type":27,"value":3484},{"type":22,"tag":896,"props":7326,"children":7327},{"class":898,"line":3505},[7328,7332],{"type":22,"tag":896,"props":7329,"children":7330},{"style":1691},[7331],{"type":27,"value":2945},{"type":22,"tag":896,"props":7333,"children":7334},{"style":903},[7335],{"type":27,"value":7336}," token;\n",{"type":22,"tag":896,"props":7338,"children":7339},{"class":898,"line":5985},[7340],{"type":22,"tag":896,"props":7341,"children":7342},{"style":903},[7343],{"type":27,"value":7344},"      },\n",{"type":22,"tag":896,"props":7346,"children":7347},{"class":898,"line":5993},[7348],{"type":22,"tag":896,"props":7349,"children":7350},{"style":903},[7351],{"type":27,"value":7352},"    },\n",{"type":22,"tag":896,"props":7354,"children":7355},{"class":898,"line":6007},[7356],{"type":22,"tag":896,"props":7357,"children":7358},{"style":903},[7359],{"type":27,"value":5887},{"type":22,"tag":896,"props":7361,"children":7362},{"class":898,"line":6059},[7363],{"type":22,"tag":896,"props":7364,"children":7365},{"style":2070},[7366],{"type":27,"value":7367},"  // Add a decorator so that routes can reference this via `fastify.authenticated`\n",{"type":22,"tag":896,"props":7369,"children":7370},{"class":898,"line":6094},[7371,7375,7379,7384,7388,7393,7397,7401,7405,7409,7413,7417,7422],{"type":22,"tag":896,"props":7372,"children":7373},{"style":1691},[7374],{"type":27,"value":5895},{"type":22,"tag":896,"props":7376,"children":7377},{"style":903},[7378],{"type":27,"value":6389},{"type":22,"tag":896,"props":7380,"children":7381},{"style":933},[7382],{"type":27,"value":7383},"decorate",{"type":22,"tag":896,"props":7385,"children":7386},{"style":903},[7387],{"type":27,"value":2888},{"type":22,"tag":896,"props":7389,"children":7390},{"style":944},[7391],{"type":27,"value":7392},"\"authenticated\"",{"type":22,"tag":896,"props":7394,"children":7395},{"style":903},[7396],{"type":27,"value":236},{"type":22,"tag":896,"props":7398,"children":7399},{"style":1691},[7400],{"type":27,"value":5561},{"type":22,"tag":896,"props":7402,"children":7403},{"style":1691},[7404],{"type":27,"value":5566},{"type":22,"tag":896,"props":7406,"children":7407},{"style":903},[7408],{"type":27,"value":2888},{"type":22,"tag":896,"props":7410,"children":7411},{"style":6025},[7412],{"type":27,"value":7064},{"type":22,"tag":896,"props":7414,"children":7415},{"style":903},[7416],{"type":27,"value":236},{"type":22,"tag":896,"props":7418,"children":7419},{"style":6025},[7420],{"type":27,"value":7421},"reply",{"type":22,"tag":896,"props":7423,"children":7424},{"style":903},[7425],{"type":27,"value":6377},{"type":22,"tag":896,"props":7427,"children":7428},{"class":898,"line":6125},[7429,7434],{"type":22,"tag":896,"props":7430,"children":7431},{"style":1691},[7432],{"type":27,"value":7433},"    try",{"type":22,"tag":896,"props":7435,"children":7436},{"style":903},[7437],{"type":27,"value":1626},{"type":22,"tag":896,"props":7439,"children":7441},{"class":898,"line":7440},32,[7442,7447,7452,7457],{"type":22,"tag":896,"props":7443,"children":7444},{"style":1691},[7445],{"type":27,"value":7446},"      await",{"type":22,"tag":896,"props":7448,"children":7449},{"style":903},[7450],{"type":27,"value":7451}," request.",{"type":22,"tag":896,"props":7453,"children":7454},{"style":933},[7455],{"type":27,"value":7456},"jwtVerify",{"type":22,"tag":896,"props":7458,"children":7459},{"style":903},[7460],{"type":27,"value":4896},{"type":22,"tag":896,"props":7462,"children":7464},{"class":898,"line":7463},33,[7465,7470,7474],{"type":22,"tag":896,"props":7466,"children":7467},{"style":903},[7468],{"type":27,"value":7469},"    } ",{"type":22,"tag":896,"props":7471,"children":7472},{"style":1691},[7473],{"type":27,"value":6136},{"type":22,"tag":896,"props":7475,"children":7476},{"style":903},[7477],{"type":27,"value":7478}," (err) {\n",{"type":22,"tag":896,"props":7480,"children":7482},{"class":898,"line":7481},34,[7483,7488,7493],{"type":22,"tag":896,"props":7484,"children":7485},{"style":903},[7486],{"type":27,"value":7487},"      reply.",{"type":22,"tag":896,"props":7489,"children":7490},{"style":933},[7491],{"type":27,"value":7492},"send",{"type":22,"tag":896,"props":7494,"children":7495},{"style":903},[7496],{"type":27,"value":7497},"(err);\n",{"type":22,"tag":896,"props":7499,"children":7501},{"class":898,"line":7500},35,[7502],{"type":22,"tag":896,"props":7503,"children":7504},{"style":903},[7505],{"type":27,"value":7506},"    }\n",{"type":22,"tag":896,"props":7508,"children":7510},{"class":898,"line":7509},36,[7511],{"type":22,"tag":896,"props":7512,"children":7513},{"style":903},[7514],{"type":27,"value":5887},{"type":22,"tag":896,"props":7516,"children":7518},{"class":898,"line":7517},37,[7519],{"type":22,"tag":896,"props":7520,"children":7521},{"style":2070},[7522],{"type":27,"value":7523},"  // Add a decorator so that routes can reference this via `fastify.authenticatedOptional`\n",{"type":22,"tag":896,"props":7525,"children":7527},{"class":898,"line":7526},38,[7528,7532,7536,7540,7544,7549,7553,7557,7561,7565,7569,7573,7577],{"type":22,"tag":896,"props":7529,"children":7530},{"style":1691},[7531],{"type":27,"value":5895},{"type":22,"tag":896,"props":7533,"children":7534},{"style":903},[7535],{"type":27,"value":6389},{"type":22,"tag":896,"props":7537,"children":7538},{"style":933},[7539],{"type":27,"value":7383},{"type":22,"tag":896,"props":7541,"children":7542},{"style":903},[7543],{"type":27,"value":2888},{"type":22,"tag":896,"props":7545,"children":7546},{"style":944},[7547],{"type":27,"value":7548},"\"authenticatedOptional\"",{"type":22,"tag":896,"props":7550,"children":7551},{"style":903},[7552],{"type":27,"value":236},{"type":22,"tag":896,"props":7554,"children":7555},{"style":1691},[7556],{"type":27,"value":5561},{"type":22,"tag":896,"props":7558,"children":7559},{"style":1691},[7560],{"type":27,"value":5566},{"type":22,"tag":896,"props":7562,"children":7563},{"style":903},[7564],{"type":27,"value":2888},{"type":22,"tag":896,"props":7566,"children":7567},{"style":6025},[7568],{"type":27,"value":7064},{"type":22,"tag":896,"props":7570,"children":7571},{"style":903},[7572],{"type":27,"value":236},{"type":22,"tag":896,"props":7574,"children":7575},{"style":6025},[7576],{"type":27,"value":7421},{"type":22,"tag":896,"props":7578,"children":7579},{"style":903},[7580],{"type":27,"value":6377},{"type":22,"tag":896,"props":7582,"children":7584},{"class":898,"line":7583},39,[7585,7589],{"type":22,"tag":896,"props":7586,"children":7587},{"style":1691},[7588],{"type":27,"value":7433},{"type":22,"tag":896,"props":7590,"children":7591},{"style":903},[7592],{"type":27,"value":1626},{"type":22,"tag":896,"props":7594,"children":7596},{"class":898,"line":7595},40,[7597,7601,7605,7609],{"type":22,"tag":896,"props":7598,"children":7599},{"style":1691},[7600],{"type":27,"value":7446},{"type":22,"tag":896,"props":7602,"children":7603},{"style":903},[7604],{"type":27,"value":7451},{"type":22,"tag":896,"props":7606,"children":7607},{"style":933},[7608],{"type":27,"value":7456},{"type":22,"tag":896,"props":7610,"children":7611},{"style":903},[7612],{"type":27,"value":4896},{"type":22,"tag":896,"props":7614,"children":7616},{"class":898,"line":7615},41,[7617,7621,7625],{"type":22,"tag":896,"props":7618,"children":7619},{"style":903},[7620],{"type":27,"value":7469},{"type":22,"tag":896,"props":7622,"children":7623},{"style":1691},[7624],{"type":27,"value":6136},{"type":22,"tag":896,"props":7626,"children":7627},{"style":903},[7628],{"type":27,"value":7629}," {}\n",{"type":22,"tag":896,"props":7631,"children":7633},{"class":898,"line":7632},42,[7634],{"type":22,"tag":896,"props":7635,"children":7636},{"style":903},[7637],{"type":27,"value":5887},{"type":22,"tag":896,"props":7639,"children":7641},{"class":898,"line":7640},43,[7642],{"type":22,"tag":896,"props":7643,"children":7644},{"style":903},[7645],{"type":27,"value":5450},{"type":22,"tag":23,"props":7647,"children":7648},{},[7649,7651,7656],{"type":27,"value":7650},"To load this plugin, ",{"type":22,"tag":486,"props":7652,"children":7654},{"className":7653},[],[7655],{"type":27,"value":4544},{"type":27,"value":7657}," will need to register it:",{"type":22,"tag":886,"props":7659,"children":7661},{"className":2505,"code":7660,"language":2507,"meta":8,"style":8},"  await app.register(require('@fastify/formbody'));\n  await app.register(require('./plugins/auth'));  // \u003C-- Register the plugin\n  await app.register(require('./routes/legacy'), {isProduction});\n  return app;\n",[7662],{"type":22,"tag":486,"props":7663,"children":7664},{"__ignoreMap":8},[7665,7700,7742,7777],{"type":22,"tag":896,"props":7666,"children":7667},{"class":898,"line":899},[7668,7672,7676,7680,7684,7688,7692,7696],{"type":22,"tag":896,"props":7669,"children":7670},{"style":1691},[7671],{"type":27,"value":5895},{"type":22,"tag":896,"props":7673,"children":7674},{"style":903},[7675],{"type":27,"value":5361},{"type":22,"tag":896,"props":7677,"children":7678},{"style":933},[7679],{"type":27,"value":5904},{"type":22,"tag":896,"props":7681,"children":7682},{"style":903},[7683],{"type":27,"value":2888},{"type":22,"tag":896,"props":7685,"children":7686},{"style":933},[7687],{"type":27,"value":4962},{"type":22,"tag":896,"props":7689,"children":7690},{"style":903},[7691],{"type":27,"value":2888},{"type":22,"tag":896,"props":7693,"children":7694},{"style":944},[7695],{"type":27,"value":5921},{"type":22,"tag":896,"props":7697,"children":7698},{"style":903},[7699],{"type":27,"value":4986},{"type":22,"tag":896,"props":7701,"children":7702},{"class":898,"line":758},[7703,7707,7711,7715,7719,7723,7727,7732,7737],{"type":22,"tag":896,"props":7704,"children":7705},{"style":1691},[7706],{"type":27,"value":5895},{"type":22,"tag":896,"props":7708,"children":7709},{"style":903},[7710],{"type":27,"value":5361},{"type":22,"tag":896,"props":7712,"children":7713},{"style":933},[7714],{"type":27,"value":5904},{"type":22,"tag":896,"props":7716,"children":7717},{"style":903},[7718],{"type":27,"value":2888},{"type":22,"tag":896,"props":7720,"children":7721},{"style":933},[7722],{"type":27,"value":4962},{"type":22,"tag":896,"props":7724,"children":7725},{"style":903},[7726],{"type":27,"value":2888},{"type":22,"tag":896,"props":7728,"children":7729},{"style":944},[7730],{"type":27,"value":7731},"'./plugins/auth'",{"type":22,"tag":896,"props":7733,"children":7734},{"style":903},[7735],{"type":27,"value":7736},"));  ",{"type":22,"tag":896,"props":7738,"children":7739},{"style":2070},[7740],{"type":27,"value":7741},"// \u003C-- Register the plugin\n",{"type":22,"tag":896,"props":7743,"children":7744},{"class":898,"line":755},[7745,7749,7753,7757,7761,7765,7769,7773],{"type":22,"tag":896,"props":7746,"children":7747},{"style":1691},[7748],{"type":27,"value":5895},{"type":22,"tag":896,"props":7750,"children":7751},{"style":903},[7752],{"type":27,"value":5361},{"type":22,"tag":896,"props":7754,"children":7755},{"style":933},[7756],{"type":27,"value":5904},{"type":22,"tag":896,"props":7758,"children":7759},{"style":903},[7760],{"type":27,"value":2888},{"type":22,"tag":896,"props":7762,"children":7763},{"style":933},[7764],{"type":27,"value":4962},{"type":22,"tag":896,"props":7766,"children":7767},{"style":903},[7768],{"type":27,"value":2888},{"type":22,"tag":896,"props":7770,"children":7771},{"style":944},[7772],{"type":27,"value":5957},{"type":22,"tag":896,"props":7774,"children":7775},{"style":903},[7776],{"type":27,"value":5962},{"type":22,"tag":896,"props":7778,"children":7779},{"class":898,"line":983},[7780,7784],{"type":22,"tag":896,"props":7781,"children":7782},{"style":1691},[7783],{"type":27,"value":5970},{"type":22,"tag":896,"props":7785,"children":7786},{"style":903},[7787],{"type":27,"value":5975},{"type":22,"tag":23,"props":7789,"children":7790},{},[7791,7793,7799,7801,7807,7809,7815],{"type":27,"value":7792},"With these decorators added, a route can now add a ",{"type":22,"tag":486,"props":7794,"children":7796},{"className":7795},[],[7797],{"type":27,"value":7798},"onRequest: [fastify.authenticated]",{"type":27,"value":7800}," or a ",{"type":22,"tag":486,"props":7802,"children":7804},{"className":7803},[],[7805],{"type":27,"value":7806},"onRequest: [fastify.authenticatedOptional]",{"type":27,"value":7808}," hook to handle authentication and to make user information available in the ",{"type":22,"tag":486,"props":7810,"children":7812},{"className":7811},[],[7813],{"type":27,"value":7814},"request.payload",{"type":27,"value":7816}," field.",{"type":22,"tag":1481,"props":7818,"children":7820},{"id":7819},"prerequisite-2-setting-up-route-plugins",[7821],{"type":27,"value":7822},"Prerequisite #2: Setting up route plugins",{"type":22,"tag":23,"props":7824,"children":7825},{},[7826,7828,7834],{"type":27,"value":7827},"Adding a route to a Fastify instance is no different from adding any other piece of functionality to a Fastify instance, which is what plugins are designed to help organize. As such, routes can be defined as a nested tree of plugins. The now-legacy ",{"type":22,"tag":486,"props":7829,"children":7831},{"className":7830},[],[7832],{"type":27,"value":7833},"articles.js",{"type":27,"value":7835}," module is as good a place as any to start converting routes, so to set up an equivalent module for the Fastify routes, we'll:",{"type":22,"tag":847,"props":7837,"children":7838},{},[7839,8020,8161,8299],{"type":22,"tag":115,"props":7840,"children":7841},{},[7842,7844,7850,7852,7857,7858],{"type":27,"value":7843},"Import and register a ",{"type":22,"tag":486,"props":7845,"children":7847},{"className":7846},[],[7848],{"type":27,"value":7849},"routes",{"type":27,"value":7851}," plugin in ",{"type":22,"tag":486,"props":7853,"children":7855},{"className":7854},[],[7856],{"type":27,"value":4544},{"type":27,"value":6801},{"type":22,"tag":886,"props":7859,"children":7861},{"className":2505,"code":7860,"language":2507,"meta":8,"style":8},"  await app.register(require('@fastify/formbody'));\n  await app.register(require('./plugins/auth'));\n  await app.register(require('./routes'));  // \u003C-- Register the plugin\n  await app.register(require('./routes/legacy'), {isProduction});\n  return app;\n",[7862],{"type":22,"tag":486,"props":7863,"children":7864},{"__ignoreMap":8},[7865,7900,7935,7974,8009],{"type":22,"tag":896,"props":7866,"children":7867},{"class":898,"line":899},[7868,7872,7876,7880,7884,7888,7892,7896],{"type":22,"tag":896,"props":7869,"children":7870},{"style":1691},[7871],{"type":27,"value":5895},{"type":22,"tag":896,"props":7873,"children":7874},{"style":903},[7875],{"type":27,"value":5361},{"type":22,"tag":896,"props":7877,"children":7878},{"style":933},[7879],{"type":27,"value":5904},{"type":22,"tag":896,"props":7881,"children":7882},{"style":903},[7883],{"type":27,"value":2888},{"type":22,"tag":896,"props":7885,"children":7886},{"style":933},[7887],{"type":27,"value":4962},{"type":22,"tag":896,"props":7889,"children":7890},{"style":903},[7891],{"type":27,"value":2888},{"type":22,"tag":896,"props":7893,"children":7894},{"style":944},[7895],{"type":27,"value":5921},{"type":22,"tag":896,"props":7897,"children":7898},{"style":903},[7899],{"type":27,"value":4986},{"type":22,"tag":896,"props":7901,"children":7902},{"class":898,"line":758},[7903,7907,7911,7915,7919,7923,7927,7931],{"type":22,"tag":896,"props":7904,"children":7905},{"style":1691},[7906],{"type":27,"value":5895},{"type":22,"tag":896,"props":7908,"children":7909},{"style":903},[7910],{"type":27,"value":5361},{"type":22,"tag":896,"props":7912,"children":7913},{"style":933},[7914],{"type":27,"value":5904},{"type":22,"tag":896,"props":7916,"children":7917},{"style":903},[7918],{"type":27,"value":2888},{"type":22,"tag":896,"props":7920,"children":7921},{"style":933},[7922],{"type":27,"value":4962},{"type":22,"tag":896,"props":7924,"children":7925},{"style":903},[7926],{"type":27,"value":2888},{"type":22,"tag":896,"props":7928,"children":7929},{"style":944},[7930],{"type":27,"value":7731},{"type":22,"tag":896,"props":7932,"children":7933},{"style":903},[7934],{"type":27,"value":4986},{"type":22,"tag":896,"props":7936,"children":7937},{"class":898,"line":755},[7938,7942,7946,7950,7954,7958,7962,7966,7970],{"type":22,"tag":896,"props":7939,"children":7940},{"style":1691},[7941],{"type":27,"value":5895},{"type":22,"tag":896,"props":7943,"children":7944},{"style":903},[7945],{"type":27,"value":5361},{"type":22,"tag":896,"props":7947,"children":7948},{"style":933},[7949],{"type":27,"value":5904},{"type":22,"tag":896,"props":7951,"children":7952},{"style":903},[7953],{"type":27,"value":2888},{"type":22,"tag":896,"props":7955,"children":7956},{"style":933},[7957],{"type":27,"value":4962},{"type":22,"tag":896,"props":7959,"children":7960},{"style":903},[7961],{"type":27,"value":2888},{"type":22,"tag":896,"props":7963,"children":7964},{"style":944},[7965],{"type":27,"value":5310},{"type":22,"tag":896,"props":7967,"children":7968},{"style":903},[7969],{"type":27,"value":7736},{"type":22,"tag":896,"props":7971,"children":7972},{"style":2070},[7973],{"type":27,"value":7741},{"type":22,"tag":896,"props":7975,"children":7976},{"class":898,"line":983},[7977,7981,7985,7989,7993,7997,8001,8005],{"type":22,"tag":896,"props":7978,"children":7979},{"style":1691},[7980],{"type":27,"value":5895},{"type":22,"tag":896,"props":7982,"children":7983},{"style":903},[7984],{"type":27,"value":5361},{"type":22,"tag":896,"props":7986,"children":7987},{"style":933},[7988],{"type":27,"value":5904},{"type":22,"tag":896,"props":7990,"children":7991},{"style":903},[7992],{"type":27,"value":2888},{"type":22,"tag":896,"props":7994,"children":7995},{"style":933},[7996],{"type":27,"value":4962},{"type":22,"tag":896,"props":7998,"children":7999},{"style":903},[8000],{"type":27,"value":2888},{"type":22,"tag":896,"props":8002,"children":8003},{"style":944},[8004],{"type":27,"value":5957},{"type":22,"tag":896,"props":8006,"children":8007},{"style":903},[8008],{"type":27,"value":5962},{"type":22,"tag":896,"props":8010,"children":8011},{"class":898,"line":1013},[8012,8016],{"type":22,"tag":896,"props":8013,"children":8014},{"style":1691},[8015],{"type":27,"value":5970},{"type":22,"tag":896,"props":8017,"children":8018},{"style":903},[8019],{"type":27,"value":5975},{"type":22,"tag":115,"props":8021,"children":8022},{},[8023,8025,8031,8033,8039,8041,8148,8152,8154,8159],{"type":27,"value":8024},"In ",{"type":22,"tag":486,"props":8026,"children":8028},{"className":8027},[],[8029],{"type":27,"value":8030},"routes/index.js",{"type":27,"value":8032},", import and register an ",{"type":22,"tag":486,"props":8034,"children":8036},{"className":8035},[],[8037],{"type":27,"value":8038},"api",{"type":27,"value":8040}," plugin:",{"type":22,"tag":886,"props":8042,"children":8044},{"className":2505,"code":8043,"language":2507,"meta":8,"style":8},"module.exports = async function (fastify, opts) {\n  await fastify.register(require('./api'), {prefix: \"/api\"});\n};\n",[8045],{"type":22,"tag":486,"props":8046,"children":8047},{"__ignoreMap":8},[8048,8096,8141],{"type":22,"tag":896,"props":8049,"children":8050},{"class":898,"line":899},[8051,8055,8059,8063,8067,8072,8076,8080,8084,8088,8092],{"type":22,"tag":896,"props":8052,"children":8053},{"style":1632},[8054],{"type":27,"value":6324},{"type":22,"tag":896,"props":8056,"children":8057},{"style":903},[8058],{"type":27,"value":71},{"type":22,"tag":896,"props":8060,"children":8061},{"style":1632},[8062],{"type":27,"value":6333},{"type":22,"tag":896,"props":8064,"children":8065},{"style":1691},[8066],{"type":27,"value":3638},{"type":22,"tag":896,"props":8068,"children":8069},{"style":1691},[8070],{"type":27,"value":8071}," async",{"type":22,"tag":896,"props":8073,"children":8074},{"style":1691},[8075],{"type":27,"value":5566},{"type":22,"tag":896,"props":8077,"children":8078},{"style":903},[8079],{"type":27,"value":3643},{"type":22,"tag":896,"props":8081,"children":8082},{"style":6025},[8083],{"type":27,"value":6363},{"type":22,"tag":896,"props":8085,"children":8086},{"style":903},[8087],{"type":27,"value":236},{"type":22,"tag":896,"props":8089,"children":8090},{"style":6025},[8091],{"type":27,"value":6372},{"type":22,"tag":896,"props":8093,"children":8094},{"style":903},[8095],{"type":27,"value":6377},{"type":22,"tag":896,"props":8097,"children":8098},{"class":898,"line":758},[8099,8103,8107,8111,8115,8119,8123,8127,8132,8137],{"type":22,"tag":896,"props":8100,"children":8101},{"style":1691},[8102],{"type":27,"value":5895},{"type":22,"tag":896,"props":8104,"children":8105},{"style":903},[8106],{"type":27,"value":6389},{"type":22,"tag":896,"props":8108,"children":8109},{"style":933},[8110],{"type":27,"value":5904},{"type":22,"tag":896,"props":8112,"children":8113},{"style":903},[8114],{"type":27,"value":2888},{"type":22,"tag":896,"props":8116,"children":8117},{"style":933},[8118],{"type":27,"value":4962},{"type":22,"tag":896,"props":8120,"children":8121},{"style":903},[8122],{"type":27,"value":2888},{"type":22,"tag":896,"props":8124,"children":8125},{"style":944},[8126],{"type":27,"value":6625},{"type":22,"tag":896,"props":8128,"children":8129},{"style":903},[8130],{"type":27,"value":8131},"), {prefix: ",{"type":22,"tag":896,"props":8133,"children":8134},{"style":944},[8135],{"type":27,"value":8136},"\"/api\"",{"type":22,"tag":896,"props":8138,"children":8139},{"style":903},[8140],{"type":27,"value":5450},{"type":22,"tag":896,"props":8142,"children":8143},{"class":898,"line":755},[8144],{"type":22,"tag":896,"props":8145,"children":8146},{"style":903},[8147],{"type":27,"value":4116},{"type":22,"tag":8149,"props":8150,"children":8151},"br",{},[],{"type":27,"value":8153},"(Note how this plugin is registered with a ",{"type":22,"tag":486,"props":8155,"children":8157},{"className":8156},[],[8158],{"type":27,"value":8136},{"type":27,"value":8160}," prefix, so that all routes within will automatically have this prefixed to their route path)",{"type":22,"tag":115,"props":8162,"children":8163},{},[8164,8165,8171,8173,8179,8181,8287,8290,8292,8297],{"type":27,"value":8024},{"type":22,"tag":486,"props":8166,"children":8168},{"className":8167},[],[8169],{"type":27,"value":8170},"routes/api/index.js",{"type":27,"value":8172},", import and register the ",{"type":22,"tag":486,"props":8174,"children":8176},{"className":8175},[],[8177],{"type":27,"value":8178},"articles",{"type":27,"value":8180}," plugin that will contain our migrated routes:",{"type":22,"tag":886,"props":8182,"children":8184},{"className":2505,"code":8183,"language":2507,"meta":8,"style":8},"module.exports = async function (fastify, opts) {\n  await fastify.register(require('./articles'), {prefix: \"/articles\"});\n}\n",[8185],{"type":22,"tag":486,"props":8186,"children":8187},{"__ignoreMap":8},[8188,8235,8280],{"type":22,"tag":896,"props":8189,"children":8190},{"class":898,"line":899},[8191,8195,8199,8203,8207,8211,8215,8219,8223,8227,8231],{"type":22,"tag":896,"props":8192,"children":8193},{"style":1632},[8194],{"type":27,"value":6324},{"type":22,"tag":896,"props":8196,"children":8197},{"style":903},[8198],{"type":27,"value":71},{"type":22,"tag":896,"props":8200,"children":8201},{"style":1632},[8202],{"type":27,"value":6333},{"type":22,"tag":896,"props":8204,"children":8205},{"style":1691},[8206],{"type":27,"value":3638},{"type":22,"tag":896,"props":8208,"children":8209},{"style":1691},[8210],{"type":27,"value":8071},{"type":22,"tag":896,"props":8212,"children":8213},{"style":1691},[8214],{"type":27,"value":5566},{"type":22,"tag":896,"props":8216,"children":8217},{"style":903},[8218],{"type":27,"value":3643},{"type":22,"tag":896,"props":8220,"children":8221},{"style":6025},[8222],{"type":27,"value":6363},{"type":22,"tag":896,"props":8224,"children":8225},{"style":903},[8226],{"type":27,"value":236},{"type":22,"tag":896,"props":8228,"children":8229},{"style":6025},[8230],{"type":27,"value":6372},{"type":22,"tag":896,"props":8232,"children":8233},{"style":903},[8234],{"type":27,"value":6377},{"type":22,"tag":896,"props":8236,"children":8237},{"class":898,"line":758},[8238,8242,8246,8250,8254,8258,8262,8267,8271,8276],{"type":22,"tag":896,"props":8239,"children":8240},{"style":1691},[8241],{"type":27,"value":5895},{"type":22,"tag":896,"props":8243,"children":8244},{"style":903},[8245],{"type":27,"value":6389},{"type":22,"tag":896,"props":8247,"children":8248},{"style":933},[8249],{"type":27,"value":5904},{"type":22,"tag":896,"props":8251,"children":8252},{"style":903},[8253],{"type":27,"value":2888},{"type":22,"tag":896,"props":8255,"children":8256},{"style":933},[8257],{"type":27,"value":4962},{"type":22,"tag":896,"props":8259,"children":8260},{"style":903},[8261],{"type":27,"value":2888},{"type":22,"tag":896,"props":8263,"children":8264},{"style":944},[8265],{"type":27,"value":8266},"'./articles'",{"type":22,"tag":896,"props":8268,"children":8269},{"style":903},[8270],{"type":27,"value":8131},{"type":22,"tag":896,"props":8272,"children":8273},{"style":944},[8274],{"type":27,"value":8275},"\"/articles\"",{"type":22,"tag":896,"props":8277,"children":8278},{"style":903},[8279],{"type":27,"value":5450},{"type":22,"tag":896,"props":8281,"children":8282},{"class":898,"line":755},[8283],{"type":22,"tag":896,"props":8284,"children":8285},{"style":903},[8286],{"type":27,"value":1745},{"type":22,"tag":8149,"props":8288,"children":8289},{},[],{"type":27,"value":8291},"(Note again the automatic ",{"type":22,"tag":486,"props":8293,"children":8295},{"className":8294},[],[8296],{"type":27,"value":8275},{"type":27,"value":8298}," prefix which will be applied to those routes)",{"type":22,"tag":115,"props":8300,"children":8301},{},[8302,8304,8310,8312],{"type":27,"value":8303},"Finally, ",{"type":22,"tag":486,"props":8305,"children":8307},{"className":8306},[],[8308],{"type":27,"value":8309},"routes/api/articles.js",{"type":27,"value":8311}," can start as an empty plugin, ready for us to define routes within:",{"type":22,"tag":886,"props":8313,"children":8315},{"className":2505,"code":8314,"language":2507,"meta":8,"style":8},"module.exports = async function (fastify, opts) {\n}\n",[8316],{"type":22,"tag":486,"props":8317,"children":8318},{"__ignoreMap":8},[8319,8366],{"type":22,"tag":896,"props":8320,"children":8321},{"class":898,"line":899},[8322,8326,8330,8334,8338,8342,8346,8350,8354,8358,8362],{"type":22,"tag":896,"props":8323,"children":8324},{"style":1632},[8325],{"type":27,"value":6324},{"type":22,"tag":896,"props":8327,"children":8328},{"style":903},[8329],{"type":27,"value":71},{"type":22,"tag":896,"props":8331,"children":8332},{"style":1632},[8333],{"type":27,"value":6333},{"type":22,"tag":896,"props":8335,"children":8336},{"style":1691},[8337],{"type":27,"value":3638},{"type":22,"tag":896,"props":8339,"children":8340},{"style":1691},[8341],{"type":27,"value":8071},{"type":22,"tag":896,"props":8343,"children":8344},{"style":1691},[8345],{"type":27,"value":5566},{"type":22,"tag":896,"props":8347,"children":8348},{"style":903},[8349],{"type":27,"value":3643},{"type":22,"tag":896,"props":8351,"children":8352},{"style":6025},[8353],{"type":27,"value":6363},{"type":22,"tag":896,"props":8355,"children":8356},{"style":903},[8357],{"type":27,"value":236},{"type":22,"tag":896,"props":8359,"children":8360},{"style":6025},[8361],{"type":27,"value":6372},{"type":22,"tag":896,"props":8363,"children":8364},{"style":903},[8365],{"type":27,"value":6377},{"type":22,"tag":896,"props":8367,"children":8368},{"class":898,"line":758},[8369],{"type":22,"tag":896,"props":8370,"children":8371},{"style":903},[8372],{"type":27,"value":1745},{"type":22,"tag":1481,"props":8374,"children":8376},{"id":8375},"migrating-a-route",[8377],{"type":27,"value":8378},"Migrating a route",{"type":22,"tag":23,"props":8380,"children":8381},{},[8382,8384,8389,8391,8397],{"type":27,"value":8383},"With the Fastify ",{"type":22,"tag":486,"props":8385,"children":8387},{"className":8386},[],[8388],{"type":27,"value":8178},{"type":27,"value":8390}," plugin ready to accept new routes, let's look at the first Express route, ",{"type":22,"tag":486,"props":8392,"children":8394},{"className":8393},[],[8395],{"type":27,"value":8396},"GET /api/articles",{"type":27,"value":8398},", piece by piece:",{"type":22,"tag":23,"props":8400,"children":8401},{},[8402,8408],{"type":22,"tag":486,"props":8403,"children":8405},{"className":8404},[],[8406],{"type":27,"value":8407},"routes/legacy/api/articles.js",{"type":27,"value":6801},{"type":22,"tag":886,"props":8410,"children":8412},{"className":2505,"code":8411,"language":2507,"meta":8,"style":8},"/* ... */\n\nrouter.get('/', auth.optional, function(req, res, next) {\n  /* ... */\n});\n\n",[8413],{"type":22,"tag":486,"props":8414,"children":8415},{"__ignoreMap":8},[8416,8423,8430,8492,8500],{"type":22,"tag":896,"props":8417,"children":8418},{"class":898,"line":899},[8419],{"type":22,"tag":896,"props":8420,"children":8421},{"style":2070},[8422],{"type":27,"value":4816},{"type":22,"tag":896,"props":8424,"children":8425},{"class":898,"line":758},[8426],{"type":22,"tag":896,"props":8427,"children":8428},{"emptyLinePlaceholder":3481},[8429],{"type":27,"value":3484},{"type":22,"tag":896,"props":8431,"children":8432},{"class":898,"line":755},[8433,8438,8443,8447,8452,8457,8461,8465,8470,8474,8479,8483,8488],{"type":22,"tag":896,"props":8434,"children":8435},{"style":903},[8436],{"type":27,"value":8437},"router.",{"type":22,"tag":896,"props":8439,"children":8440},{"style":933},[8441],{"type":27,"value":8442},"get",{"type":22,"tag":896,"props":8444,"children":8445},{"style":903},[8446],{"type":27,"value":2888},{"type":22,"tag":896,"props":8448,"children":8449},{"style":944},[8450],{"type":27,"value":8451},"'/'",{"type":22,"tag":896,"props":8453,"children":8454},{"style":903},[8455],{"type":27,"value":8456},", auth.optional, ",{"type":22,"tag":896,"props":8458,"children":8459},{"style":1691},[8460],{"type":27,"value":5395},{"type":22,"tag":896,"props":8462,"children":8463},{"style":903},[8464],{"type":27,"value":2888},{"type":22,"tag":896,"props":8466,"children":8467},{"style":6025},[8468],{"type":27,"value":8469},"req",{"type":22,"tag":896,"props":8471,"children":8472},{"style":903},[8473],{"type":27,"value":236},{"type":22,"tag":896,"props":8475,"children":8476},{"style":6025},[8477],{"type":27,"value":8478},"res",{"type":22,"tag":896,"props":8480,"children":8481},{"style":903},[8482],{"type":27,"value":236},{"type":22,"tag":896,"props":8484,"children":8485},{"style":6025},[8486],{"type":27,"value":8487},"next",{"type":22,"tag":896,"props":8489,"children":8490},{"style":903},[8491],{"type":27,"value":6377},{"type":22,"tag":896,"props":8493,"children":8494},{"class":898,"line":983},[8495],{"type":22,"tag":896,"props":8496,"children":8497},{"style":2070},[8498],{"type":27,"value":8499},"  /* ... */\n",{"type":22,"tag":896,"props":8501,"children":8502},{"class":898,"line":1013},[8503],{"type":22,"tag":896,"props":8504,"children":8505},{"style":903},[8506],{"type":27,"value":5450},{"type":22,"tag":23,"props":8508,"children":8509},{},[8510,8512,8518,8520,8526],{"type":27,"value":8511},"Fastify does have a ",{"type":22,"tag":486,"props":8513,"children":8515},{"className":8514},[],[8516],{"type":27,"value":8517},".get()",{"type":27,"value":8519}," shorthand like this, but I recommend getting used to using the full ",{"type":22,"tag":486,"props":8521,"children":8523},{"className":8522},[],[8524],{"type":27,"value":8525},".route()",{"type":27,"value":8527}," declaration since I find it more readable when other options are defined:",{"type":22,"tag":23,"props":8529,"children":8530},{},[8531,8536],{"type":22,"tag":486,"props":8532,"children":8534},{"className":8533},[],[8535],{"type":27,"value":8309},{"type":27,"value":6801},{"type":22,"tag":886,"props":8538,"children":8540},{"className":2505,"code":8539,"language":2507,"meta":8,"style":8},"  /* ... */\n\n  fastify.route({\n    method: 'GET',\n    url: '/',\n    onRequest: [fastify.authenticatedOptional],\n    handler: async function(request, reply) {\n      /* ... */\n    },\n  });\n\n  /* ... */\n",[8541],{"type":22,"tag":486,"props":8542,"children":8543},{"__ignoreMap":8},[8544,8551,8558,8575,8592,8608,8616,8656,8664,8671,8678,8685],{"type":22,"tag":896,"props":8545,"children":8546},{"class":898,"line":899},[8547],{"type":22,"tag":896,"props":8548,"children":8549},{"style":2070},[8550],{"type":27,"value":8499},{"type":22,"tag":896,"props":8552,"children":8553},{"class":898,"line":758},[8554],{"type":22,"tag":896,"props":8555,"children":8556},{"emptyLinePlaceholder":3481},[8557],{"type":27,"value":3484},{"type":22,"tag":896,"props":8559,"children":8560},{"class":898,"line":755},[8561,8565,8570],{"type":22,"tag":896,"props":8562,"children":8563},{"style":903},[8564],{"type":27,"value":6462},{"type":22,"tag":896,"props":8566,"children":8567},{"style":933},[8568],{"type":27,"value":8569},"route",{"type":22,"tag":896,"props":8571,"children":8572},{"style":903},[8573],{"type":27,"value":8574},"({\n",{"type":22,"tag":896,"props":8576,"children":8577},{"class":898,"line":983},[8578,8583,8588],{"type":22,"tag":896,"props":8579,"children":8580},{"style":903},[8581],{"type":27,"value":8582},"    method: ",{"type":22,"tag":896,"props":8584,"children":8585},{"style":944},[8586],{"type":27,"value":8587},"'GET'",{"type":22,"tag":896,"props":8589,"children":8590},{"style":903},[8591],{"type":27,"value":3988},{"type":22,"tag":896,"props":8593,"children":8594},{"class":898,"line":1013},[8595,8600,8604],{"type":22,"tag":896,"props":8596,"children":8597},{"style":903},[8598],{"type":27,"value":8599},"    url: ",{"type":22,"tag":896,"props":8601,"children":8602},{"style":944},[8603],{"type":27,"value":8451},{"type":22,"tag":896,"props":8605,"children":8606},{"style":903},[8607],{"type":27,"value":3988},{"type":22,"tag":896,"props":8609,"children":8610},{"class":898,"line":1030},[8611],{"type":22,"tag":896,"props":8612,"children":8613},{"style":903},[8614],{"type":27,"value":8615},"    onRequest: [fastify.authenticatedOptional],\n",{"type":22,"tag":896,"props":8617,"children":8618},{"class":898,"line":1068},[8619,8624,8628,8632,8636,8640,8644,8648,8652],{"type":22,"tag":896,"props":8620,"children":8621},{"style":933},[8622],{"type":27,"value":8623},"    handler",{"type":22,"tag":896,"props":8625,"children":8626},{"style":903},[8627],{"type":27,"value":1640},{"type":22,"tag":896,"props":8629,"children":8630},{"style":1691},[8631],{"type":27,"value":5561},{"type":22,"tag":896,"props":8633,"children":8634},{"style":1691},[8635],{"type":27,"value":5566},{"type":22,"tag":896,"props":8637,"children":8638},{"style":903},[8639],{"type":27,"value":2888},{"type":22,"tag":896,"props":8641,"children":8642},{"style":6025},[8643],{"type":27,"value":7064},{"type":22,"tag":896,"props":8645,"children":8646},{"style":903},[8647],{"type":27,"value":236},{"type":22,"tag":896,"props":8649,"children":8650},{"style":6025},[8651],{"type":27,"value":7421},{"type":22,"tag":896,"props":8653,"children":8654},{"style":903},[8655],{"type":27,"value":6377},{"type":22,"tag":896,"props":8657,"children":8658},{"class":898,"line":1085},[8659],{"type":22,"tag":896,"props":8660,"children":8661},{"style":2070},[8662],{"type":27,"value":8663},"      /* ... */\n",{"type":22,"tag":896,"props":8665,"children":8666},{"class":898,"line":1123},[8667],{"type":22,"tag":896,"props":8668,"children":8669},{"style":903},[8670],{"type":27,"value":7352},{"type":22,"tag":896,"props":8672,"children":8673},{"class":898,"line":1161},[8674],{"type":22,"tag":896,"props":8675,"children":8676},{"style":903},[8677],{"type":27,"value":5887},{"type":22,"tag":896,"props":8679,"children":8680},{"class":898,"line":2298},[8681],{"type":22,"tag":896,"props":8682,"children":8683},{"emptyLinePlaceholder":3481},[8684],{"type":27,"value":3484},{"type":22,"tag":896,"props":8686,"children":8687},{"class":898,"line":3170},[8688],{"type":22,"tag":896,"props":8689,"children":8690},{"style":2070},[8691],{"type":27,"value":8499},{"type":22,"tag":23,"props":8693,"children":8694},{},[8695,8697,8703,8705,8711],{"type":27,"value":8696},"Note the ",{"type":22,"tag":486,"props":8698,"children":8700},{"className":8699},[],[8701],{"type":27,"value":8702},"onRequest",{"type":27,"value":8704}," hook which uses the authentication hook and decorator we defined previously in the ",{"type":22,"tag":486,"props":8706,"children":8708},{"className":8707},[],[8709],{"type":27,"value":8710},"auth",{"type":27,"value":8712}," plugin.",{"type":22,"tag":23,"props":8714,"children":8715},{},[8716],{"type":27,"value":8717},"Next, the Express route checks for a number of query parameters and fetches some database objects:",{"type":22,"tag":23,"props":8719,"children":8720},{},[8721,8726],{"type":22,"tag":486,"props":8722,"children":8724},{"className":8723},[],[8725],{"type":27,"value":8407},{"type":27,"value":6801},{"type":22,"tag":886,"props":8728,"children":8730},{"className":2505,"code":8729,"language":2507,"meta":8,"style":8},"  /* ... */\n\n  var query = {};\n  var limit = 20;\n  var offset = 0;\n\n  if(typeof req.query.limit !== 'undefined'){\n    limit = req.query.limit;\n  }\n\n  if(typeof req.query.offset !== 'undefined'){\n    offset = req.query.offset;\n  }\n\n  if( typeof req.query.tag !== 'undefined' ){\n    query.tagList = {\"$in\" : [req.query.tag]};\n  }\n\n  Promise.all([\n    req.query.author ? User.findOne({username: req.query.author}) : null,\n    req.query.favorited ? User.findOne({username: req.query.favorited}) : null\n  ]).then(function(results){\n    /* ... */\n  });\n\n  /* ... */\n",[8731],{"type":22,"tag":486,"props":8732,"children":8733},{"__ignoreMap":8},[8734,8741,8748,8770,8795,8819,8826,8862,8879,8886,8893,8925,8942,8949,8956,8990,9017,9024,9031,9053,9094,9128,9161,9169,9176,9183],{"type":22,"tag":896,"props":8735,"children":8736},{"class":898,"line":899},[8737],{"type":22,"tag":896,"props":8738,"children":8739},{"style":2070},[8740],{"type":27,"value":8499},{"type":22,"tag":896,"props":8742,"children":8743},{"class":898,"line":758},[8744],{"type":22,"tag":896,"props":8745,"children":8746},{"emptyLinePlaceholder":3481},[8747],{"type":27,"value":3484},{"type":22,"tag":896,"props":8749,"children":8750},{"class":898,"line":755},[8751,8756,8761,8765],{"type":22,"tag":896,"props":8752,"children":8753},{"style":1691},[8754],{"type":27,"value":8755},"  var",{"type":22,"tag":896,"props":8757,"children":8758},{"style":903},[8759],{"type":27,"value":8760}," query ",{"type":22,"tag":896,"props":8762,"children":8763},{"style":1691},[8764],{"type":27,"value":941},{"type":22,"tag":896,"props":8766,"children":8767},{"style":903},[8768],{"type":27,"value":8769}," {};\n",{"type":22,"tag":896,"props":8771,"children":8772},{"class":898,"line":983},[8773,8777,8782,8786,8791],{"type":22,"tag":896,"props":8774,"children":8775},{"style":1691},[8776],{"type":27,"value":8755},{"type":22,"tag":896,"props":8778,"children":8779},{"style":903},[8780],{"type":27,"value":8781}," limit ",{"type":22,"tag":896,"props":8783,"children":8784},{"style":1691},[8785],{"type":27,"value":941},{"type":22,"tag":896,"props":8787,"children":8788},{"style":1632},[8789],{"type":27,"value":8790}," 20",{"type":22,"tag":896,"props":8792,"children":8793},{"style":903},[8794],{"type":27,"value":1649},{"type":22,"tag":896,"props":8796,"children":8797},{"class":898,"line":1013},[8798,8802,8807,8811,8815],{"type":22,"tag":896,"props":8799,"children":8800},{"style":1691},[8801],{"type":27,"value":8755},{"type":22,"tag":896,"props":8803,"children":8804},{"style":903},[8805],{"type":27,"value":8806}," offset ",{"type":22,"tag":896,"props":8808,"children":8809},{"style":1691},[8810],{"type":27,"value":941},{"type":22,"tag":896,"props":8812,"children":8813},{"style":1632},[8814],{"type":27,"value":2933},{"type":22,"tag":896,"props":8816,"children":8817},{"style":903},[8818],{"type":27,"value":1649},{"type":22,"tag":896,"props":8820,"children":8821},{"class":898,"line":1030},[8822],{"type":22,"tag":896,"props":8823,"children":8824},{"emptyLinePlaceholder":3481},[8825],{"type":27,"value":3484},{"type":22,"tag":896,"props":8827,"children":8828},{"class":898,"line":1068},[8829,8833,8837,8842,8847,8852,8857],{"type":22,"tag":896,"props":8830,"children":8831},{"style":1691},[8832],{"type":27,"value":5624},{"type":22,"tag":896,"props":8834,"children":8835},{"style":903},[8836],{"type":27,"value":2888},{"type":22,"tag":896,"props":8838,"children":8839},{"style":1691},[8840],{"type":27,"value":8841},"typeof",{"type":22,"tag":896,"props":8843,"children":8844},{"style":903},[8845],{"type":27,"value":8846}," req.query.limit ",{"type":22,"tag":896,"props":8848,"children":8849},{"style":1691},[8850],{"type":27,"value":8851},"!==",{"type":22,"tag":896,"props":8853,"children":8854},{"style":944},[8855],{"type":27,"value":8856}," 'undefined'",{"type":22,"tag":896,"props":8858,"children":8859},{"style":903},[8860],{"type":27,"value":8861},"){\n",{"type":22,"tag":896,"props":8863,"children":8864},{"class":898,"line":1085},[8865,8870,8874],{"type":22,"tag":896,"props":8866,"children":8867},{"style":903},[8868],{"type":27,"value":8869},"    limit ",{"type":22,"tag":896,"props":8871,"children":8872},{"style":1691},[8873],{"type":27,"value":941},{"type":22,"tag":896,"props":8875,"children":8876},{"style":903},[8877],{"type":27,"value":8878}," req.query.limit;\n",{"type":22,"tag":896,"props":8880,"children":8881},{"class":898,"line":1123},[8882],{"type":22,"tag":896,"props":8883,"children":8884},{"style":903},[8885],{"type":27,"value":5730},{"type":22,"tag":896,"props":8887,"children":8888},{"class":898,"line":1161},[8889],{"type":22,"tag":896,"props":8890,"children":8891},{"emptyLinePlaceholder":3481},[8892],{"type":27,"value":3484},{"type":22,"tag":896,"props":8894,"children":8895},{"class":898,"line":2298},[8896,8900,8904,8908,8913,8917,8921],{"type":22,"tag":896,"props":8897,"children":8898},{"style":1691},[8899],{"type":27,"value":5624},{"type":22,"tag":896,"props":8901,"children":8902},{"style":903},[8903],{"type":27,"value":2888},{"type":22,"tag":896,"props":8905,"children":8906},{"style":1691},[8907],{"type":27,"value":8841},{"type":22,"tag":896,"props":8909,"children":8910},{"style":903},[8911],{"type":27,"value":8912}," req.query.offset ",{"type":22,"tag":896,"props":8914,"children":8915},{"style":1691},[8916],{"type":27,"value":8851},{"type":22,"tag":896,"props":8918,"children":8919},{"style":944},[8920],{"type":27,"value":8856},{"type":22,"tag":896,"props":8922,"children":8923},{"style":903},[8924],{"type":27,"value":8861},{"type":22,"tag":896,"props":8926,"children":8927},{"class":898,"line":3170},[8928,8933,8937],{"type":22,"tag":896,"props":8929,"children":8930},{"style":903},[8931],{"type":27,"value":8932},"    offset ",{"type":22,"tag":896,"props":8934,"children":8935},{"style":1691},[8936],{"type":27,"value":941},{"type":22,"tag":896,"props":8938,"children":8939},{"style":903},[8940],{"type":27,"value":8941}," req.query.offset;\n",{"type":22,"tag":896,"props":8943,"children":8944},{"class":898,"line":3187},[8945],{"type":22,"tag":896,"props":8946,"children":8947},{"style":903},[8948],{"type":27,"value":5730},{"type":22,"tag":896,"props":8950,"children":8951},{"class":898,"line":3228},[8952],{"type":22,"tag":896,"props":8953,"children":8954},{"emptyLinePlaceholder":3481},[8955],{"type":27,"value":3484},{"type":22,"tag":896,"props":8957,"children":8958},{"class":898,"line":3245},[8959,8963,8968,8972,8977,8981,8985],{"type":22,"tag":896,"props":8960,"children":8961},{"style":1691},[8962],{"type":27,"value":5624},{"type":22,"tag":896,"props":8964,"children":8965},{"style":903},[8966],{"type":27,"value":8967},"( ",{"type":22,"tag":896,"props":8969,"children":8970},{"style":1691},[8971],{"type":27,"value":8841},{"type":22,"tag":896,"props":8973,"children":8974},{"style":903},[8975],{"type":27,"value":8976}," req.query.tag ",{"type":22,"tag":896,"props":8978,"children":8979},{"style":1691},[8980],{"type":27,"value":8851},{"type":22,"tag":896,"props":8982,"children":8983},{"style":944},[8984],{"type":27,"value":8856},{"type":22,"tag":896,"props":8986,"children":8987},{"style":903},[8988],{"type":27,"value":8989}," ){\n",{"type":22,"tag":896,"props":8991,"children":8992},{"class":898,"line":3286},[8993,8998,9002,9007,9012],{"type":22,"tag":896,"props":8994,"children":8995},{"style":903},[8996],{"type":27,"value":8997},"    query.tagList ",{"type":22,"tag":896,"props":8999,"children":9000},{"style":1691},[9001],{"type":27,"value":941},{"type":22,"tag":896,"props":9003,"children":9004},{"style":903},[9005],{"type":27,"value":9006}," {",{"type":22,"tag":896,"props":9008,"children":9009},{"style":944},[9010],{"type":27,"value":9011},"\"$in\"",{"type":22,"tag":896,"props":9013,"children":9014},{"style":903},[9015],{"type":27,"value":9016}," : [req.query.tag]};\n",{"type":22,"tag":896,"props":9018,"children":9019},{"class":898,"line":3303},[9020],{"type":22,"tag":896,"props":9021,"children":9022},{"style":903},[9023],{"type":27,"value":5730},{"type":22,"tag":896,"props":9025,"children":9026},{"class":898,"line":3344},[9027],{"type":22,"tag":896,"props":9028,"children":9029},{"emptyLinePlaceholder":3481},[9030],{"type":27,"value":3484},{"type":22,"tag":896,"props":9032,"children":9033},{"class":898,"line":3361},[9034,9039,9043,9048],{"type":22,"tag":896,"props":9035,"children":9036},{"style":1632},[9037],{"type":27,"value":9038},"  Promise",{"type":22,"tag":896,"props":9040,"children":9041},{"style":903},[9042],{"type":27,"value":71},{"type":22,"tag":896,"props":9044,"children":9045},{"style":933},[9046],{"type":27,"value":9047},"all",{"type":22,"tag":896,"props":9049,"children":9050},{"style":903},[9051],{"type":27,"value":9052},"([\n",{"type":22,"tag":896,"props":9054,"children":9055},{"class":898,"line":3402},[9056,9061,9066,9071,9076,9081,9085,9090],{"type":22,"tag":896,"props":9057,"children":9058},{"style":903},[9059],{"type":27,"value":9060},"    req.query.author ",{"type":22,"tag":896,"props":9062,"children":9063},{"style":1691},[9064],{"type":27,"value":9065},"?",{"type":22,"tag":896,"props":9067,"children":9068},{"style":903},[9069],{"type":27,"value":9070}," User.",{"type":22,"tag":896,"props":9072,"children":9073},{"style":933},[9074],{"type":27,"value":9075},"findOne",{"type":22,"tag":896,"props":9077,"children":9078},{"style":903},[9079],{"type":27,"value":9080},"({username: req.query.author}) ",{"type":22,"tag":896,"props":9082,"children":9083},{"style":1691},[9084],{"type":27,"value":6801},{"type":22,"tag":896,"props":9086,"children":9087},{"style":1632},[9088],{"type":27,"value":9089}," null",{"type":22,"tag":896,"props":9091,"children":9092},{"style":903},[9093],{"type":27,"value":3988},{"type":22,"tag":896,"props":9095,"children":9096},{"class":898,"line":3419},[9097,9102,9106,9110,9114,9119,9123],{"type":22,"tag":896,"props":9098,"children":9099},{"style":903},[9100],{"type":27,"value":9101},"    req.query.favorited ",{"type":22,"tag":896,"props":9103,"children":9104},{"style":1691},[9105],{"type":27,"value":9065},{"type":22,"tag":896,"props":9107,"children":9108},{"style":903},[9109],{"type":27,"value":9070},{"type":22,"tag":896,"props":9111,"children":9112},{"style":933},[9113],{"type":27,"value":9075},{"type":22,"tag":896,"props":9115,"children":9116},{"style":903},[9117],{"type":27,"value":9118},"({username: req.query.favorited}) ",{"type":22,"tag":896,"props":9120,"children":9121},{"style":1691},[9122],{"type":27,"value":6801},{"type":22,"tag":896,"props":9124,"children":9125},{"style":1632},[9126],{"type":27,"value":9127}," null\n",{"type":22,"tag":896,"props":9129,"children":9130},{"class":898,"line":3460},[9131,9136,9140,9144,9148,9152,9157],{"type":22,"tag":896,"props":9132,"children":9133},{"style":903},[9134],{"type":27,"value":9135},"  ]).",{"type":22,"tag":896,"props":9137,"children":9138},{"style":933},[9139],{"type":27,"value":6018},{"type":22,"tag":896,"props":9141,"children":9142},{"style":903},[9143],{"type":27,"value":2888},{"type":22,"tag":896,"props":9145,"children":9146},{"style":1691},[9147],{"type":27,"value":5395},{"type":22,"tag":896,"props":9149,"children":9150},{"style":903},[9151],{"type":27,"value":2888},{"type":22,"tag":896,"props":9153,"children":9154},{"style":6025},[9155],{"type":27,"value":9156},"results",{"type":22,"tag":896,"props":9158,"children":9159},{"style":903},[9160],{"type":27,"value":8861},{"type":22,"tag":896,"props":9162,"children":9163},{"class":898,"line":3477},[9164],{"type":22,"tag":896,"props":9165,"children":9166},{"style":2070},[9167],{"type":27,"value":9168},"    /* ... */\n",{"type":22,"tag":896,"props":9170,"children":9171},{"class":898,"line":3487},[9172],{"type":22,"tag":896,"props":9173,"children":9174},{"style":903},[9175],{"type":27,"value":5887},{"type":22,"tag":896,"props":9177,"children":9178},{"class":898,"line":3505},[9179],{"type":22,"tag":896,"props":9180,"children":9181},{"emptyLinePlaceholder":3481},[9182],{"type":27,"value":3484},{"type":22,"tag":896,"props":9184,"children":9185},{"class":898,"line":5985},[9186],{"type":22,"tag":896,"props":9187,"children":9188},{"style":2070},[9189],{"type":27,"value":8499},{"type":22,"tag":23,"props":9191,"children":9192},{},[9193],{"type":27,"value":9194},"This is a place where beyond just replicating the Express route's behaviour, the Fastify route can easily improve upon it by adding more thorough schema validation for these parameters:",{"type":22,"tag":23,"props":9196,"children":9197},{},[9198,9203],{"type":22,"tag":486,"props":9199,"children":9201},{"className":9200},[],[9202],{"type":27,"value":8309},{"type":27,"value":6801},{"type":22,"tag":886,"props":9205,"children":9207},{"className":2505,"code":9206,"language":2507,"meta":8,"style":8},"  fastify.route({\n    method: 'GET',\n    url: '/',\n    onRequest: [fastify.authenticatedOptional],\n    schema: {   // \u003C--- `schema` field added\n      query: {  // \u003C--- query param validation added (also works for body, params, headers)\n        type: \"object\",\n        properties: {\n          limit: {type: \"number\", minimum: 1},\n          offset: {type: \"number\", minimum: 0},\n          tag: {type: \"string\", minLength: 1},\n          author: {type: \"string\", minLength: 1},\n          favorited: {type: \"string\", minLength: 1},\n        },\n        required: [],\n      },\n    },\n    handler: async function(request, reply) {\n      /* ... */\n    },\n  });\n\n  /* ... */\n",[9208],{"type":22,"tag":486,"props":9209,"children":9210},{"__ignoreMap":8},[9211,9226,9241,9256,9263,9276,9289,9306,9314,9342,9367,9393,9417,9441,9449,9457,9464,9471,9510,9517,9524,9531,9538],{"type":22,"tag":896,"props":9212,"children":9213},{"class":898,"line":899},[9214,9218,9222],{"type":22,"tag":896,"props":9215,"children":9216},{"style":903},[9217],{"type":27,"value":6462},{"type":22,"tag":896,"props":9219,"children":9220},{"style":933},[9221],{"type":27,"value":8569},{"type":22,"tag":896,"props":9223,"children":9224},{"style":903},[9225],{"type":27,"value":8574},{"type":22,"tag":896,"props":9227,"children":9228},{"class":898,"line":758},[9229,9233,9237],{"type":22,"tag":896,"props":9230,"children":9231},{"style":903},[9232],{"type":27,"value":8582},{"type":22,"tag":896,"props":9234,"children":9235},{"style":944},[9236],{"type":27,"value":8587},{"type":22,"tag":896,"props":9238,"children":9239},{"style":903},[9240],{"type":27,"value":3988},{"type":22,"tag":896,"props":9242,"children":9243},{"class":898,"line":755},[9244,9248,9252],{"type":22,"tag":896,"props":9245,"children":9246},{"style":903},[9247],{"type":27,"value":8599},{"type":22,"tag":896,"props":9249,"children":9250},{"style":944},[9251],{"type":27,"value":8451},{"type":22,"tag":896,"props":9253,"children":9254},{"style":903},[9255],{"type":27,"value":3988},{"type":22,"tag":896,"props":9257,"children":9258},{"class":898,"line":983},[9259],{"type":22,"tag":896,"props":9260,"children":9261},{"style":903},[9262],{"type":27,"value":8615},{"type":22,"tag":896,"props":9264,"children":9265},{"class":898,"line":1013},[9266,9271],{"type":22,"tag":896,"props":9267,"children":9268},{"style":903},[9269],{"type":27,"value":9270},"    schema: {   ",{"type":22,"tag":896,"props":9272,"children":9273},{"style":2070},[9274],{"type":27,"value":9275},"// \u003C--- `schema` field added\n",{"type":22,"tag":896,"props":9277,"children":9278},{"class":898,"line":1030},[9279,9284],{"type":22,"tag":896,"props":9280,"children":9281},{"style":903},[9282],{"type":27,"value":9283},"      query: {  ",{"type":22,"tag":896,"props":9285,"children":9286},{"style":2070},[9287],{"type":27,"value":9288},"// \u003C--- query param validation added (also works for body, params, headers)\n",{"type":22,"tag":896,"props":9290,"children":9291},{"class":898,"line":1068},[9292,9297,9302],{"type":22,"tag":896,"props":9293,"children":9294},{"style":903},[9295],{"type":27,"value":9296},"        type: ",{"type":22,"tag":896,"props":9298,"children":9299},{"style":944},[9300],{"type":27,"value":9301},"\"object\"",{"type":22,"tag":896,"props":9303,"children":9304},{"style":903},[9305],{"type":27,"value":3988},{"type":22,"tag":896,"props":9307,"children":9308},{"class":898,"line":1085},[9309],{"type":22,"tag":896,"props":9310,"children":9311},{"style":903},[9312],{"type":27,"value":9313},"        properties: {\n",{"type":22,"tag":896,"props":9315,"children":9316},{"class":898,"line":1123},[9317,9322,9327,9332,9337],{"type":22,"tag":896,"props":9318,"children":9319},{"style":903},[9320],{"type":27,"value":9321},"          limit: {type: ",{"type":22,"tag":896,"props":9323,"children":9324},{"style":944},[9325],{"type":27,"value":9326},"\"number\"",{"type":22,"tag":896,"props":9328,"children":9329},{"style":903},[9330],{"type":27,"value":9331},", minimum: ",{"type":22,"tag":896,"props":9333,"children":9334},{"style":1632},[9335],{"type":27,"value":9336},"1",{"type":22,"tag":896,"props":9338,"children":9339},{"style":903},[9340],{"type":27,"value":9341},"},\n",{"type":22,"tag":896,"props":9343,"children":9344},{"class":898,"line":1161},[9345,9350,9354,9358,9363],{"type":22,"tag":896,"props":9346,"children":9347},{"style":903},[9348],{"type":27,"value":9349},"          offset: {type: ",{"type":22,"tag":896,"props":9351,"children":9352},{"style":944},[9353],{"type":27,"value":9326},{"type":22,"tag":896,"props":9355,"children":9356},{"style":903},[9357],{"type":27,"value":9331},{"type":22,"tag":896,"props":9359,"children":9360},{"style":1632},[9361],{"type":27,"value":9362},"0",{"type":22,"tag":896,"props":9364,"children":9365},{"style":903},[9366],{"type":27,"value":9341},{"type":22,"tag":896,"props":9368,"children":9369},{"class":898,"line":2298},[9370,9375,9380,9385,9389],{"type":22,"tag":896,"props":9371,"children":9372},{"style":903},[9373],{"type":27,"value":9374},"          tag: {type: ",{"type":22,"tag":896,"props":9376,"children":9377},{"style":944},[9378],{"type":27,"value":9379},"\"string\"",{"type":22,"tag":896,"props":9381,"children":9382},{"style":903},[9383],{"type":27,"value":9384},", minLength: ",{"type":22,"tag":896,"props":9386,"children":9387},{"style":1632},[9388],{"type":27,"value":9336},{"type":22,"tag":896,"props":9390,"children":9391},{"style":903},[9392],{"type":27,"value":9341},{"type":22,"tag":896,"props":9394,"children":9395},{"class":898,"line":3170},[9396,9401,9405,9409,9413],{"type":22,"tag":896,"props":9397,"children":9398},{"style":903},[9399],{"type":27,"value":9400},"          author: {type: ",{"type":22,"tag":896,"props":9402,"children":9403},{"style":944},[9404],{"type":27,"value":9379},{"type":22,"tag":896,"props":9406,"children":9407},{"style":903},[9408],{"type":27,"value":9384},{"type":22,"tag":896,"props":9410,"children":9411},{"style":1632},[9412],{"type":27,"value":9336},{"type":22,"tag":896,"props":9414,"children":9415},{"style":903},[9416],{"type":27,"value":9341},{"type":22,"tag":896,"props":9418,"children":9419},{"class":898,"line":3187},[9420,9425,9429,9433,9437],{"type":22,"tag":896,"props":9421,"children":9422},{"style":903},[9423],{"type":27,"value":9424},"          favorited: {type: ",{"type":22,"tag":896,"props":9426,"children":9427},{"style":944},[9428],{"type":27,"value":9379},{"type":22,"tag":896,"props":9430,"children":9431},{"style":903},[9432],{"type":27,"value":9384},{"type":22,"tag":896,"props":9434,"children":9435},{"style":1632},[9436],{"type":27,"value":9336},{"type":22,"tag":896,"props":9438,"children":9439},{"style":903},[9440],{"type":27,"value":9341},{"type":22,"tag":896,"props":9442,"children":9443},{"class":898,"line":3228},[9444],{"type":22,"tag":896,"props":9445,"children":9446},{"style":903},[9447],{"type":27,"value":9448},"        },\n",{"type":22,"tag":896,"props":9450,"children":9451},{"class":898,"line":3245},[9452],{"type":22,"tag":896,"props":9453,"children":9454},{"style":903},[9455],{"type":27,"value":9456},"        required: [],\n",{"type":22,"tag":896,"props":9458,"children":9459},{"class":898,"line":3286},[9460],{"type":22,"tag":896,"props":9461,"children":9462},{"style":903},[9463],{"type":27,"value":7344},{"type":22,"tag":896,"props":9465,"children":9466},{"class":898,"line":3303},[9467],{"type":22,"tag":896,"props":9468,"children":9469},{"style":903},[9470],{"type":27,"value":7352},{"type":22,"tag":896,"props":9472,"children":9473},{"class":898,"line":3344},[9474,9478,9482,9486,9490,9494,9498,9502,9506],{"type":22,"tag":896,"props":9475,"children":9476},{"style":933},[9477],{"type":27,"value":8623},{"type":22,"tag":896,"props":9479,"children":9480},{"style":903},[9481],{"type":27,"value":1640},{"type":22,"tag":896,"props":9483,"children":9484},{"style":1691},[9485],{"type":27,"value":5561},{"type":22,"tag":896,"props":9487,"children":9488},{"style":1691},[9489],{"type":27,"value":5566},{"type":22,"tag":896,"props":9491,"children":9492},{"style":903},[9493],{"type":27,"value":2888},{"type":22,"tag":896,"props":9495,"children":9496},{"style":6025},[9497],{"type":27,"value":7064},{"type":22,"tag":896,"props":9499,"children":9500},{"style":903},[9501],{"type":27,"value":236},{"type":22,"tag":896,"props":9503,"children":9504},{"style":6025},[9505],{"type":27,"value":7421},{"type":22,"tag":896,"props":9507,"children":9508},{"style":903},[9509],{"type":27,"value":6377},{"type":22,"tag":896,"props":9511,"children":9512},{"class":898,"line":3361},[9513],{"type":22,"tag":896,"props":9514,"children":9515},{"style":2070},[9516],{"type":27,"value":8663},{"type":22,"tag":896,"props":9518,"children":9519},{"class":898,"line":3402},[9520],{"type":22,"tag":896,"props":9521,"children":9522},{"style":903},[9523],{"type":27,"value":7352},{"type":22,"tag":896,"props":9525,"children":9526},{"class":898,"line":3419},[9527],{"type":22,"tag":896,"props":9528,"children":9529},{"style":903},[9530],{"type":27,"value":5887},{"type":22,"tag":896,"props":9532,"children":9533},{"class":898,"line":3460},[9534],{"type":22,"tag":896,"props":9535,"children":9536},{"emptyLinePlaceholder":3481},[9537],{"type":27,"value":3484},{"type":22,"tag":896,"props":9539,"children":9540},{"class":898,"line":3477},[9541],{"type":22,"tag":896,"props":9542,"children":9543},{"style":2070},[9544],{"type":27,"value":8499},{"type":22,"tag":23,"props":9546,"children":9547},{},[9548,9550,9555,9557,9562,9564,9570],{"type":27,"value":9549},"Otherwise, the body of this route can be migrated largely unchanged, except with the ",{"type":22,"tag":486,"props":9551,"children":9553},{"className":9552},[],[9554],{"type":27,"value":8469},{"type":27,"value":9556}," variable renamed to ",{"type":22,"tag":486,"props":9558,"children":9560},{"className":9559},[],[9561],{"type":27,"value":7064},{"type":27,"value":9563}," (a purely stylistic choice but one which seems to be the convention in Fastify). The only other difference is where the response is returned; in Express ",{"type":22,"tag":486,"props":9565,"children":9567},{"className":9566},[],[9568],{"type":27,"value":9569},"res.json()",{"type":27,"value":9571}," is called, but Fastify is JSON-enabled by default and so a JavaScript object can just be returned directly.",{"type":22,"tag":23,"props":9573,"children":9574},{},[9575,9577,9583,9585,9591,9593,9599,9601,9607],{"type":27,"value":9576},"The rest of the routes in this module can be migrated similarly, with one exception: some rely on an Express utility called ",{"type":22,"tag":486,"props":9578,"children":9580},{"className":9579},[],[9581],{"type":27,"value":9582},".param()",{"type":27,"value":9584},", for which Fastify doesn't have a direct equivalent. This utility is used in order to automatically fetch an ",{"type":22,"tag":486,"props":9586,"children":9588},{"className":9587},[],[9589],{"type":27,"value":9590},"Article",{"type":27,"value":9592}," object from the database for any route which has a ",{"type":22,"tag":486,"props":9594,"children":9596},{"className":9595},[],[9597],{"type":27,"value":9598},":article",{"type":27,"value":9600}," query parameter, and make it available to the route handler as ",{"type":22,"tag":486,"props":9602,"children":9604},{"className":9603},[],[9605],{"type":27,"value":9606},"req.article",{"type":27,"value":6801},{"type":22,"tag":23,"props":9609,"children":9610},{},[9611,9616],{"type":22,"tag":486,"props":9612,"children":9614},{"className":9613},[],[9615],{"type":27,"value":8407},{"type":27,"value":6801},{"type":22,"tag":886,"props":9618,"children":9620},{"className":2505,"code":9619,"language":2507,"meta":8,"style":8},"router.param('article', function(req, res, next, slug) {\n  Article.findOne({ slug: slug})\n    .populate('author')\n    .then(function (article) {\n      if (!article) { return res.sendStatus(404); }\n\n      req.article = article;\n\n      return next();\n    }).catch(next);\n});\n",[9621],{"type":22,"tag":486,"props":9622,"children":9623},{"__ignoreMap":8},[9624,9690,9707,9733,9765,9815,9822,9839,9846,9863,9880],{"type":22,"tag":896,"props":9625,"children":9626},{"class":898,"line":899},[9627,9631,9636,9640,9645,9649,9653,9657,9661,9665,9669,9673,9677,9681,9686],{"type":22,"tag":896,"props":9628,"children":9629},{"style":903},[9630],{"type":27,"value":8437},{"type":22,"tag":896,"props":9632,"children":9633},{"style":933},[9634],{"type":27,"value":9635},"param",{"type":22,"tag":896,"props":9637,"children":9638},{"style":903},[9639],{"type":27,"value":2888},{"type":22,"tag":896,"props":9641,"children":9642},{"style":944},[9643],{"type":27,"value":9644},"'article'",{"type":22,"tag":896,"props":9646,"children":9647},{"style":903},[9648],{"type":27,"value":236},{"type":22,"tag":896,"props":9650,"children":9651},{"style":1691},[9652],{"type":27,"value":5395},{"type":22,"tag":896,"props":9654,"children":9655},{"style":903},[9656],{"type":27,"value":2888},{"type":22,"tag":896,"props":9658,"children":9659},{"style":6025},[9660],{"type":27,"value":8469},{"type":22,"tag":896,"props":9662,"children":9663},{"style":903},[9664],{"type":27,"value":236},{"type":22,"tag":896,"props":9666,"children":9667},{"style":6025},[9668],{"type":27,"value":8478},{"type":22,"tag":896,"props":9670,"children":9671},{"style":903},[9672],{"type":27,"value":236},{"type":22,"tag":896,"props":9674,"children":9675},{"style":6025},[9676],{"type":27,"value":8487},{"type":22,"tag":896,"props":9678,"children":9679},{"style":903},[9680],{"type":27,"value":236},{"type":22,"tag":896,"props":9682,"children":9683},{"style":6025},[9684],{"type":27,"value":9685},"slug",{"type":22,"tag":896,"props":9687,"children":9688},{"style":903},[9689],{"type":27,"value":6377},{"type":22,"tag":896,"props":9691,"children":9692},{"class":898,"line":758},[9693,9698,9702],{"type":22,"tag":896,"props":9694,"children":9695},{"style":903},[9696],{"type":27,"value":9697},"  Article.",{"type":22,"tag":896,"props":9699,"children":9700},{"style":933},[9701],{"type":27,"value":9075},{"type":22,"tag":896,"props":9703,"children":9704},{"style":903},[9705],{"type":27,"value":9706},"({ slug: slug})\n",{"type":22,"tag":896,"props":9708,"children":9709},{"class":898,"line":755},[9710,9715,9720,9724,9729],{"type":22,"tag":896,"props":9711,"children":9712},{"style":903},[9713],{"type":27,"value":9714},"    .",{"type":22,"tag":896,"props":9716,"children":9717},{"style":933},[9718],{"type":27,"value":9719},"populate",{"type":22,"tag":896,"props":9721,"children":9722},{"style":903},[9723],{"type":27,"value":2888},{"type":22,"tag":896,"props":9725,"children":9726},{"style":944},[9727],{"type":27,"value":9728},"'author'",{"type":22,"tag":896,"props":9730,"children":9731},{"style":903},[9732],{"type":27,"value":2903},{"type":22,"tag":896,"props":9734,"children":9735},{"class":898,"line":983},[9736,9740,9744,9748,9752,9756,9761],{"type":22,"tag":896,"props":9737,"children":9738},{"style":903},[9739],{"type":27,"value":9714},{"type":22,"tag":896,"props":9741,"children":9742},{"style":933},[9743],{"type":27,"value":6018},{"type":22,"tag":896,"props":9745,"children":9746},{"style":903},[9747],{"type":27,"value":2888},{"type":22,"tag":896,"props":9749,"children":9750},{"style":1691},[9751],{"type":27,"value":5395},{"type":22,"tag":896,"props":9753,"children":9754},{"style":903},[9755],{"type":27,"value":3643},{"type":22,"tag":896,"props":9757,"children":9758},{"style":6025},[9759],{"type":27,"value":9760},"article",{"type":22,"tag":896,"props":9762,"children":9763},{"style":903},[9764],{"type":27,"value":6377},{"type":22,"tag":896,"props":9766,"children":9767},{"class":898,"line":1013},[9768,9773,9777,9781,9786,9791,9796,9801,9805,9810],{"type":22,"tag":896,"props":9769,"children":9770},{"style":1691},[9771],{"type":27,"value":9772},"      if",{"type":22,"tag":896,"props":9774,"children":9775},{"style":903},[9776],{"type":27,"value":3643},{"type":22,"tag":896,"props":9778,"children":9779},{"style":1691},[9780],{"type":27,"value":7247},{"type":22,"tag":896,"props":9782,"children":9783},{"style":903},[9784],{"type":27,"value":9785},"article) { ",{"type":22,"tag":896,"props":9787,"children":9788},{"style":1691},[9789],{"type":27,"value":9790},"return",{"type":22,"tag":896,"props":9792,"children":9793},{"style":903},[9794],{"type":27,"value":9795}," res.",{"type":22,"tag":896,"props":9797,"children":9798},{"style":933},[9799],{"type":27,"value":9800},"sendStatus",{"type":22,"tag":896,"props":9802,"children":9803},{"style":903},[9804],{"type":27,"value":2888},{"type":22,"tag":896,"props":9806,"children":9807},{"style":1632},[9808],{"type":27,"value":9809},"404",{"type":22,"tag":896,"props":9811,"children":9812},{"style":903},[9813],{"type":27,"value":9814},"); }\n",{"type":22,"tag":896,"props":9816,"children":9817},{"class":898,"line":1030},[9818],{"type":22,"tag":896,"props":9819,"children":9820},{"emptyLinePlaceholder":3481},[9821],{"type":27,"value":3484},{"type":22,"tag":896,"props":9823,"children":9824},{"class":898,"line":1068},[9825,9830,9834],{"type":22,"tag":896,"props":9826,"children":9827},{"style":903},[9828],{"type":27,"value":9829},"      req.article ",{"type":22,"tag":896,"props":9831,"children":9832},{"style":1691},[9833],{"type":27,"value":941},{"type":22,"tag":896,"props":9835,"children":9836},{"style":903},[9837],{"type":27,"value":9838}," article;\n",{"type":22,"tag":896,"props":9840,"children":9841},{"class":898,"line":1085},[9842],{"type":22,"tag":896,"props":9843,"children":9844},{"emptyLinePlaceholder":3481},[9845],{"type":27,"value":3484},{"type":22,"tag":896,"props":9847,"children":9848},{"class":898,"line":1123},[9849,9854,9859],{"type":22,"tag":896,"props":9850,"children":9851},{"style":1691},[9852],{"type":27,"value":9853},"      return",{"type":22,"tag":896,"props":9855,"children":9856},{"style":933},[9857],{"type":27,"value":9858}," next",{"type":22,"tag":896,"props":9860,"children":9861},{"style":903},[9862],{"type":27,"value":4896},{"type":22,"tag":896,"props":9864,"children":9865},{"class":898,"line":1161},[9866,9871,9875],{"type":22,"tag":896,"props":9867,"children":9868},{"style":903},[9869],{"type":27,"value":9870},"    }).",{"type":22,"tag":896,"props":9872,"children":9873},{"style":933},[9874],{"type":27,"value":6136},{"type":22,"tag":896,"props":9876,"children":9877},{"style":903},[9878],{"type":27,"value":9879},"(next);\n",{"type":22,"tag":896,"props":9881,"children":9882},{"class":898,"line":2298},[9883],{"type":22,"tag":896,"props":9884,"children":9885},{"style":903},[9886],{"type":27,"value":5450},{"type":22,"tag":23,"props":9888,"children":9889},{},[9890],{"type":27,"value":9891},"It's possible to do the same in Fastify using the more generic \"hooks\" system:",{"type":22,"tag":886,"props":9893,"children":9895},{"className":2505,"code":9894,"language":2507,"meta":8,"style":8},"fastify.addHook('onRequest', async (request, reply) => {\n  if ('article' in request.params) {\n    request.article = await Article\n      .findOne({ slug: request.params.article})\n      .populate('author');\n\n    if (!request.article) {\n        reply.code(401).send();\n        return reply;\n    }\n  }\n});\n",[9896],{"type":22,"tag":486,"props":9897,"children":9898},{"__ignoreMap":8},[9899,9957,9982,10004,10021,10044,10051,10071,10104,10116,10123,10130],{"type":22,"tag":896,"props":9900,"children":9901},{"class":898,"line":899},[9902,9907,9912,9916,9921,9925,9929,9933,9937,9941,9945,9949,9953],{"type":22,"tag":896,"props":9903,"children":9904},{"style":903},[9905],{"type":27,"value":9906},"fastify.",{"type":22,"tag":896,"props":9908,"children":9909},{"style":933},[9910],{"type":27,"value":9911},"addHook",{"type":22,"tag":896,"props":9913,"children":9914},{"style":903},[9915],{"type":27,"value":2888},{"type":22,"tag":896,"props":9917,"children":9918},{"style":944},[9919],{"type":27,"value":9920},"'onRequest'",{"type":22,"tag":896,"props":9922,"children":9923},{"style":903},[9924],{"type":27,"value":236},{"type":22,"tag":896,"props":9926,"children":9927},{"style":1691},[9928],{"type":27,"value":5561},{"type":22,"tag":896,"props":9930,"children":9931},{"style":903},[9932],{"type":27,"value":3643},{"type":22,"tag":896,"props":9934,"children":9935},{"style":6025},[9936],{"type":27,"value":7064},{"type":22,"tag":896,"props":9938,"children":9939},{"style":903},[9940],{"type":27,"value":236},{"type":22,"tag":896,"props":9942,"children":9943},{"style":6025},[9944],{"type":27,"value":7421},{"type":22,"tag":896,"props":9946,"children":9947},{"style":903},[9948],{"type":27,"value":6082},{"type":22,"tag":896,"props":9950,"children":9951},{"style":1691},[9952],{"type":27,"value":6087},{"type":22,"tag":896,"props":9954,"children":9955},{"style":903},[9956],{"type":27,"value":1626},{"type":22,"tag":896,"props":9958,"children":9959},{"class":898,"line":758},[9960,9964,9968,9972,9977],{"type":22,"tag":896,"props":9961,"children":9962},{"style":1691},[9963],{"type":27,"value":5624},{"type":22,"tag":896,"props":9965,"children":9966},{"style":903},[9967],{"type":27,"value":3643},{"type":22,"tag":896,"props":9969,"children":9970},{"style":944},[9971],{"type":27,"value":9644},{"type":22,"tag":896,"props":9973,"children":9974},{"style":1691},[9975],{"type":27,"value":9976}," in",{"type":22,"tag":896,"props":9978,"children":9979},{"style":903},[9980],{"type":27,"value":9981}," request.params) {\n",{"type":22,"tag":896,"props":9983,"children":9984},{"class":898,"line":755},[9985,9990,9994,9999],{"type":22,"tag":896,"props":9986,"children":9987},{"style":903},[9988],{"type":27,"value":9989},"    request.article ",{"type":22,"tag":896,"props":9991,"children":9992},{"style":1691},[9993],{"type":27,"value":941},{"type":22,"tag":896,"props":9995,"children":9996},{"style":1691},[9997],{"type":27,"value":9998}," await",{"type":22,"tag":896,"props":10000,"children":10001},{"style":903},[10002],{"type":27,"value":10003}," Article\n",{"type":22,"tag":896,"props":10005,"children":10006},{"class":898,"line":983},[10007,10012,10016],{"type":22,"tag":896,"props":10008,"children":10009},{"style":903},[10010],{"type":27,"value":10011},"      .",{"type":22,"tag":896,"props":10013,"children":10014},{"style":933},[10015],{"type":27,"value":9075},{"type":22,"tag":896,"props":10017,"children":10018},{"style":903},[10019],{"type":27,"value":10020},"({ slug: request.params.article})\n",{"type":22,"tag":896,"props":10022,"children":10023},{"class":898,"line":1013},[10024,10028,10032,10036,10040],{"type":22,"tag":896,"props":10025,"children":10026},{"style":903},[10027],{"type":27,"value":10011},{"type":22,"tag":896,"props":10029,"children":10030},{"style":933},[10031],{"type":27,"value":9719},{"type":22,"tag":896,"props":10033,"children":10034},{"style":903},[10035],{"type":27,"value":2888},{"type":22,"tag":896,"props":10037,"children":10038},{"style":944},[10039],{"type":27,"value":9728},{"type":22,"tag":896,"props":10041,"children":10042},{"style":903},[10043],{"type":27,"value":3678},{"type":22,"tag":896,"props":10045,"children":10046},{"class":898,"line":1030},[10047],{"type":22,"tag":896,"props":10048,"children":10049},{"emptyLinePlaceholder":3481},[10050],{"type":27,"value":3484},{"type":22,"tag":896,"props":10052,"children":10053},{"class":898,"line":1068},[10054,10058,10062,10066],{"type":22,"tag":896,"props":10055,"children":10056},{"style":1691},[10057],{"type":27,"value":2918},{"type":22,"tag":896,"props":10059,"children":10060},{"style":903},[10061],{"type":27,"value":3643},{"type":22,"tag":896,"props":10063,"children":10064},{"style":1691},[10065],{"type":27,"value":7247},{"type":22,"tag":896,"props":10067,"children":10068},{"style":903},[10069],{"type":27,"value":10070},"request.article) {\n",{"type":22,"tag":896,"props":10072,"children":10073},{"class":898,"line":1085},[10074,10079,10083,10087,10092,10096,10100],{"type":22,"tag":896,"props":10075,"children":10076},{"style":903},[10077],{"type":27,"value":10078},"        reply.",{"type":22,"tag":896,"props":10080,"children":10081},{"style":933},[10082],{"type":27,"value":486},{"type":22,"tag":896,"props":10084,"children":10085},{"style":903},[10086],{"type":27,"value":2888},{"type":22,"tag":896,"props":10088,"children":10089},{"style":1632},[10090],{"type":27,"value":10091},"401",{"type":22,"tag":896,"props":10093,"children":10094},{"style":903},[10095],{"type":27,"value":290},{"type":22,"tag":896,"props":10097,"children":10098},{"style":933},[10099],{"type":27,"value":7492},{"type":22,"tag":896,"props":10101,"children":10102},{"style":903},[10103],{"type":27,"value":4896},{"type":22,"tag":896,"props":10105,"children":10106},{"class":898,"line":1123},[10107,10111],{"type":22,"tag":896,"props":10108,"children":10109},{"style":1691},[10110],{"type":27,"value":2945},{"type":22,"tag":896,"props":10112,"children":10113},{"style":903},[10114],{"type":27,"value":10115}," reply;\n",{"type":22,"tag":896,"props":10117,"children":10118},{"class":898,"line":1161},[10119],{"type":22,"tag":896,"props":10120,"children":10121},{"style":903},[10122],{"type":27,"value":7506},{"type":22,"tag":896,"props":10124,"children":10125},{"class":898,"line":2298},[10126],{"type":22,"tag":896,"props":10127,"children":10128},{"style":903},[10129],{"type":27,"value":5730},{"type":22,"tag":896,"props":10131,"children":10132},{"class":898,"line":3170},[10133],{"type":22,"tag":896,"props":10134,"children":10135},{"style":903},[10136],{"type":27,"value":5450},{"type":22,"tag":23,"props":10138,"children":10139},{},[10140],{"type":27,"value":10141},"Note that for style reasons however, I opted to remove hooks like these and perform these database requests within the route handlers themselves; this makes the code more direct and easy to reason about, and also allows us to query for the Article in parallel with other database queries.",{"type":22,"tag":23,"props":10143,"children":10144},{},[10145,10146,10152],{"type":27,"value":6732},{"type":22,"tag":30,"props":10147,"children":10150},{"href":10148,"rel":10149},"https://github.com/artandlogic/node-express-to-fastify-example/commit/de495b65e32db961d84db8c78fd96621deac8404",[34],[10151],{"type":27,"value":4369},{"type":27,"value":10153}," to view the rest of the partial conversion to Fastify. This process can continue piece by piece until the last of the Express routes are migrated.",{"type":22,"tag":193,"props":10155,"children":10157},{"id":10156},"step-3-removing-express-and-related-plugins",[10158],{"type":27,"value":10159},"Step 3: Removing Express and related plugins",{"type":22,"tag":23,"props":10161,"children":10162},{},[10163,10165,10171,10172,10177,10179,10185,10187,10193,10195,10200],{"type":27,"value":10164},"Once the migration is complete, the registration of the ",{"type":22,"tag":486,"props":10166,"children":10168},{"className":10167},[],[10169],{"type":27,"value":10170},"routes/legacy",{"type":27,"value":7851},{"type":22,"tag":486,"props":10173,"children":10175},{"className":10174},[],[10176],{"type":27,"value":4544},{"type":27,"value":10178}," can be removed, the modules in the ",{"type":22,"tag":486,"props":10180,"children":10182},{"className":10181},[],[10183],{"type":27,"value":10184},"legacy/",{"type":27,"value":10186}," directory can be deleted, and all Express-related plugins can be removed from ",{"type":22,"tag":486,"props":10188,"children":10190},{"className":10189},[],[10191],{"type":27,"value":10192},"package.json",{"type":27,"value":10194}," (including ",{"type":22,"tag":486,"props":10196,"children":10198},{"className":10197},[],[10199],{"type":27,"value":4393},{"type":27,"value":10201},"). What's left is an application that has been fully migrated to Fastify, while remaining fully functional at every step along the way.",{"type":22,"tag":23,"props":10203,"children":10204},{},[10205,10206,10212],{"type":27,"value":6732},{"type":22,"tag":30,"props":10207,"children":10210},{"href":10208,"rel":10209},"https://github.com/artandlogic/node-express-to-fastify-example/commit/25da1c0e9b0a3b7d9c6e8b102fd4112796634fdf",[34],[10211],{"type":27,"value":4369},{"type":27,"value":10213}," to view the fully-migrated codebase.",{"type":22,"tag":193,"props":10215,"children":10217},{"id":10216},"bonus-adding-integration-tests",[10218],{"type":27,"value":10219},"Bonus: Adding integration tests",{"type":22,"tag":23,"props":10221,"children":10222},{},[10223],{"type":27,"value":10224},"This project currently relies on end-to-end tests that execute against a full running instance of the application. It's valuable to have some tests like these, but most of them would be better off as integration tests, such that test cases are independent of one another and they have the ability to set up data before they run.",{"type":22,"tag":23,"props":10226,"children":10227},{},[10228],{"type":27,"value":10229},"To use Fastify's built-in testing utilities to this end, a few minor things in this project need to be refactored:",{"type":22,"tag":847,"props":10231,"children":10232},{},[10233,10579],{"type":22,"tag":115,"props":10234,"children":10235},{},[10236,10238,10243,10245,10250,10252,10255,10260,10262,10515,10518,10520,10525,10527,10532,10534,10540,10542,10547,10549,10554,10556,10562,10564,10570,10572,10577],{"type":27,"value":10237},"The ",{"type":22,"tag":486,"props":10239,"children":10241},{"className":10240},[],[10242],{"type":27,"value":4319},{"type":27,"value":10244}," instance creation needs to be sparated from the place where the it's used to start up the server itself; this is because the test utilities work on the ",{"type":22,"tag":486,"props":10246,"children":10248},{"className":10247},[],[10249],{"type":27,"value":4319},{"type":27,"value":10251}," app itself without starting up a real Webserver.",{"type":22,"tag":8149,"props":10253,"children":10254},{},[],{"type":22,"tag":486,"props":10256,"children":10258},{"className":10257},[],[10259],{"type":27,"value":4544},{"type":27,"value":10261}," is currently doing both:",{"type":22,"tag":886,"props":10263,"children":10265},{"className":2505,"code":10264,"language":2507,"meta":8,"style":8},"async function build() {\n  /* ... */\n\n  const app = require('fastify')({logger: true});\n\n  /* ... */\n\n  return app;\n}\n\nbuild()\n  .then(app => app.listen({port: 3000}))\n  .then((address) => {\n    console.log('Listening on ' + address);\n  }).catch(console.log);\n",[10266],{"type":22,"tag":486,"props":10267,"children":10268},{"__ignoreMap":8},[10269,10288,10295,10302,10342,10349,10356,10363,10374,10381,10388,10399,10442,10473,10500],{"type":22,"tag":896,"props":10270,"children":10271},{"class":898,"line":899},[10272,10276,10280,10284],{"type":22,"tag":896,"props":10273,"children":10274},{"style":1691},[10275],{"type":27,"value":5561},{"type":22,"tag":896,"props":10277,"children":10278},{"style":1691},[10279],{"type":27,"value":5566},{"type":22,"tag":896,"props":10281,"children":10282},{"style":933},[10283],{"type":27,"value":5571},{"type":22,"tag":896,"props":10285,"children":10286},{"style":903},[10287],{"type":27,"value":5576},{"type":22,"tag":896,"props":10289,"children":10290},{"class":898,"line":758},[10291],{"type":22,"tag":896,"props":10292,"children":10293},{"style":2070},[10294],{"type":27,"value":8499},{"type":22,"tag":896,"props":10296,"children":10297},{"class":898,"line":755},[10298],{"type":22,"tag":896,"props":10299,"children":10300},{"emptyLinePlaceholder":3481},[10301],{"type":27,"value":3484},{"type":22,"tag":896,"props":10303,"children":10304},{"class":898,"line":983},[10305,10309,10313,10317,10321,10325,10329,10334,10338],{"type":22,"tag":896,"props":10306,"children":10307},{"style":1691},[10308],{"type":27,"value":5584},{"type":22,"tag":896,"props":10310,"children":10311},{"style":1632},[10312],{"type":27,"value":5841},{"type":22,"tag":896,"props":10314,"children":10315},{"style":1691},[10316],{"type":27,"value":3638},{"type":22,"tag":896,"props":10318,"children":10319},{"style":933},[10320],{"type":27,"value":4696},{"type":22,"tag":896,"props":10322,"children":10323},{"style":903},[10324],{"type":27,"value":2888},{"type":22,"tag":896,"props":10326,"children":10327},{"style":944},[10328],{"type":27,"value":5858},{"type":22,"tag":896,"props":10330,"children":10331},{"style":903},[10332],{"type":27,"value":10333},")({logger: ",{"type":22,"tag":896,"props":10335,"children":10336},{"style":1632},[10337],{"type":27,"value":5177},{"type":22,"tag":896,"props":10339,"children":10340},{"style":903},[10341],{"type":27,"value":5450},{"type":22,"tag":896,"props":10343,"children":10344},{"class":898,"line":1013},[10345],{"type":22,"tag":896,"props":10346,"children":10347},{"emptyLinePlaceholder":3481},[10348],{"type":27,"value":3484},{"type":22,"tag":896,"props":10350,"children":10351},{"class":898,"line":1030},[10352],{"type":22,"tag":896,"props":10353,"children":10354},{"style":2070},[10355],{"type":27,"value":8499},{"type":22,"tag":896,"props":10357,"children":10358},{"class":898,"line":1068},[10359],{"type":22,"tag":896,"props":10360,"children":10361},{"emptyLinePlaceholder":3481},[10362],{"type":27,"value":3484},{"type":22,"tag":896,"props":10364,"children":10365},{"class":898,"line":1085},[10366,10370],{"type":22,"tag":896,"props":10367,"children":10368},{"style":1691},[10369],{"type":27,"value":5970},{"type":22,"tag":896,"props":10371,"children":10372},{"style":903},[10373],{"type":27,"value":5975},{"type":22,"tag":896,"props":10375,"children":10376},{"class":898,"line":1123},[10377],{"type":22,"tag":896,"props":10378,"children":10379},{"style":903},[10380],{"type":27,"value":1745},{"type":22,"tag":896,"props":10382,"children":10383},{"class":898,"line":1161},[10384],{"type":22,"tag":896,"props":10385,"children":10386},{"emptyLinePlaceholder":3481},[10387],{"type":27,"value":3484},{"type":22,"tag":896,"props":10389,"children":10390},{"class":898,"line":2298},[10391,10395],{"type":22,"tag":896,"props":10392,"children":10393},{"style":933},[10394],{"type":27,"value":5999},{"type":22,"tag":896,"props":10396,"children":10397},{"style":903},[10398],{"type":27,"value":6004},{"type":22,"tag":896,"props":10400,"children":10401},{"class":898,"line":3170},[10402,10406,10410,10414,10418,10422,10426,10430,10434,10438],{"type":22,"tag":896,"props":10403,"children":10404},{"style":903},[10405],{"type":27,"value":6013},{"type":22,"tag":896,"props":10407,"children":10408},{"style":933},[10409],{"type":27,"value":6018},{"type":22,"tag":896,"props":10411,"children":10412},{"style":903},[10413],{"type":27,"value":2888},{"type":22,"tag":896,"props":10415,"children":10416},{"style":6025},[10417],{"type":27,"value":6028},{"type":22,"tag":896,"props":10419,"children":10420},{"style":1691},[10421],{"type":27,"value":6033},{"type":22,"tag":896,"props":10423,"children":10424},{"style":903},[10425],{"type":27,"value":5361},{"type":22,"tag":896,"props":10427,"children":10428},{"style":933},[10429],{"type":27,"value":5366},{"type":22,"tag":896,"props":10431,"children":10432},{"style":903},[10433],{"type":27,"value":6046},{"type":22,"tag":896,"props":10435,"children":10436},{"style":1632},[10437],{"type":27,"value":6051},{"type":22,"tag":896,"props":10439,"children":10440},{"style":903},[10441],{"type":27,"value":6056},{"type":22,"tag":896,"props":10443,"children":10444},{"class":898,"line":3187},[10445,10449,10453,10457,10461,10465,10469],{"type":22,"tag":896,"props":10446,"children":10447},{"style":903},[10448],{"type":27,"value":6013},{"type":22,"tag":896,"props":10450,"children":10451},{"style":933},[10452],{"type":27,"value":6018},{"type":22,"tag":896,"props":10454,"children":10455},{"style":903},[10456],{"type":27,"value":6073},{"type":22,"tag":896,"props":10458,"children":10459},{"style":6025},[10460],{"type":27,"value":5437},{"type":22,"tag":896,"props":10462,"children":10463},{"style":903},[10464],{"type":27,"value":6082},{"type":22,"tag":896,"props":10466,"children":10467},{"style":1691},[10468],{"type":27,"value":6087},{"type":22,"tag":896,"props":10470,"children":10471},{"style":903},[10472],{"type":27,"value":1626},{"type":22,"tag":896,"props":10474,"children":10475},{"class":898,"line":3228},[10476,10480,10484,10488,10492,10496],{"type":22,"tag":896,"props":10477,"children":10478},{"style":903},[10479],{"type":27,"value":6100},{"type":22,"tag":896,"props":10481,"children":10482},{"style":933},[10483],{"type":27,"value":5413},{"type":22,"tag":896,"props":10485,"children":10486},{"style":903},[10487],{"type":27,"value":2888},{"type":22,"tag":896,"props":10489,"children":10490},{"style":944},[10491],{"type":27,"value":6113},{"type":22,"tag":896,"props":10493,"children":10494},{"style":1691},[10495],{"type":27,"value":5427},{"type":22,"tag":896,"props":10497,"children":10498},{"style":903},[10499],{"type":27,"value":6122},{"type":22,"tag":896,"props":10501,"children":10502},{"class":898,"line":3245},[10503,10507,10511],{"type":22,"tag":896,"props":10504,"children":10505},{"style":903},[10506],{"type":27,"value":6131},{"type":22,"tag":896,"props":10508,"children":10509},{"style":933},[10510],{"type":27,"value":6136},{"type":22,"tag":896,"props":10512,"children":10513},{"style":903},[10514],{"type":27,"value":6141},{"type":22,"tag":8149,"props":10516,"children":10517},{},[],{"type":27,"value":10519},"A solution is to have ",{"type":22,"tag":486,"props":10521,"children":10523},{"className":10522},[],[10524],{"type":27,"value":4544},{"type":27,"value":10526}," export its ",{"type":22,"tag":486,"props":10528,"children":10530},{"className":10529},[],[10531],{"type":27,"value":5494},{"type":27,"value":10533}," function, and then create a separate ",{"type":22,"tag":486,"props":10535,"children":10537},{"className":10536},[],[10538],{"type":27,"value":10539},"server.js",{"type":27,"value":10541}," file to move the ",{"type":22,"tag":486,"props":10543,"children":10545},{"className":10544},[],[10546],{"type":27,"value":5494},{"type":27,"value":10548}," invocation into (updating the ",{"type":22,"tag":486,"props":10550,"children":10552},{"className":10551},[],[10553],{"type":27,"value":10192},{"type":27,"value":10555}," scripts so that they invoke ",{"type":22,"tag":486,"props":10557,"children":10559},{"className":10558},[],[10560],{"type":27,"value":10561},"node ./server.js",{"type":27,"value":10563}," instead of ",{"type":22,"tag":486,"props":10565,"children":10567},{"className":10566},[],[10568],{"type":27,"value":10569},"node ./app.js",{"type":27,"value":10571},"). The behaviour is the same, but now the ",{"type":22,"tag":486,"props":10573,"children":10575},{"className":10574},[],[10576],{"type":27,"value":4319},{"type":27,"value":10578}," app can be imported in tests.",{"type":22,"tag":115,"props":10580,"children":10581},{},[10582,10584,10595,10597],{"type":27,"value":10583},"In in-memory MongoDB database needs to be configured, so that the integration tests can create one. This can be done easily with the ",{"type":22,"tag":30,"props":10585,"children":10588},{"href":10586,"rel":10587},"https://www.npmjs.com/package/mongodb-memory-server",[34],[10589],{"type":22,"tag":486,"props":10590,"children":10592},{"className":10591},[],[10593],{"type":27,"value":10594},"mongodb-memory-server",{"type":27,"value":10596}," package:",{"type":22,"tag":886,"props":10598,"children":10600},{"className":2505,"code":10599,"language":2507,"meta":8,"style":8},"const {MongoMemoryServer} = require('mongodb-memory-server');\n\nmongod = await MongoMemoryServer.create();\nprocess.env.MONGODB_URI = mongod.getUri();\n\n/* Do testing... */\n\nmongod.stop();\n",[10601],{"type":22,"tag":486,"props":10602,"children":10603},{"__ignoreMap":8},[10604,10645,10652,10682,10712,10719,10727,10734],{"type":22,"tag":896,"props":10605,"children":10606},{"class":898,"line":899},[10607,10611,10615,10620,10624,10628,10632,10636,10641],{"type":22,"tag":896,"props":10608,"children":10609},{"style":1691},[10610],{"type":27,"value":6169},{"type":22,"tag":896,"props":10612,"children":10613},{"style":903},[10614],{"type":27,"value":9006},{"type":22,"tag":896,"props":10616,"children":10617},{"style":1632},[10618],{"type":27,"value":10619},"MongoMemoryServer",{"type":22,"tag":896,"props":10621,"children":10622},{"style":903},[10623],{"type":27,"value":5114},{"type":22,"tag":896,"props":10625,"children":10626},{"style":1691},[10627],{"type":27,"value":941},{"type":22,"tag":896,"props":10629,"children":10630},{"style":933},[10631],{"type":27,"value":4696},{"type":22,"tag":896,"props":10633,"children":10634},{"style":903},[10635],{"type":27,"value":2888},{"type":22,"tag":896,"props":10637,"children":10638},{"style":944},[10639],{"type":27,"value":10640},"'mongodb-memory-server'",{"type":22,"tag":896,"props":10642,"children":10643},{"style":903},[10644],{"type":27,"value":3678},{"type":22,"tag":896,"props":10646,"children":10647},{"class":898,"line":758},[10648],{"type":22,"tag":896,"props":10649,"children":10650},{"emptyLinePlaceholder":3481},[10651],{"type":27,"value":3484},{"type":22,"tag":896,"props":10653,"children":10654},{"class":898,"line":755},[10655,10660,10664,10668,10673,10678],{"type":22,"tag":896,"props":10656,"children":10657},{"style":903},[10658],{"type":27,"value":10659},"mongod ",{"type":22,"tag":896,"props":10661,"children":10662},{"style":1691},[10663],{"type":27,"value":941},{"type":22,"tag":896,"props":10665,"children":10666},{"style":1691},[10667],{"type":27,"value":9998},{"type":22,"tag":896,"props":10669,"children":10670},{"style":903},[10671],{"type":27,"value":10672}," MongoMemoryServer.",{"type":22,"tag":896,"props":10674,"children":10675},{"style":933},[10676],{"type":27,"value":10677},"create",{"type":22,"tag":896,"props":10679,"children":10680},{"style":903},[10681],{"type":27,"value":4896},{"type":22,"tag":896,"props":10683,"children":10684},{"class":898,"line":983},[10685,10690,10694,10698,10703,10708],{"type":22,"tag":896,"props":10686,"children":10687},{"style":903},[10688],{"type":27,"value":10689},"process.env.",{"type":22,"tag":896,"props":10691,"children":10692},{"style":1632},[10693],{"type":27,"value":5102},{"type":22,"tag":896,"props":10695,"children":10696},{"style":1691},[10697],{"type":27,"value":3638},{"type":22,"tag":896,"props":10699,"children":10700},{"style":903},[10701],{"type":27,"value":10702}," mongod.",{"type":22,"tag":896,"props":10704,"children":10705},{"style":933},[10706],{"type":27,"value":10707},"getUri",{"type":22,"tag":896,"props":10709,"children":10710},{"style":903},[10711],{"type":27,"value":4896},{"type":22,"tag":896,"props":10713,"children":10714},{"class":898,"line":1013},[10715],{"type":22,"tag":896,"props":10716,"children":10717},{"emptyLinePlaceholder":3481},[10718],{"type":27,"value":3484},{"type":22,"tag":896,"props":10720,"children":10721},{"class":898,"line":1030},[10722],{"type":22,"tag":896,"props":10723,"children":10724},{"style":2070},[10725],{"type":27,"value":10726},"/* Do testing... */\n",{"type":22,"tag":896,"props":10728,"children":10729},{"class":898,"line":1068},[10730],{"type":22,"tag":896,"props":10731,"children":10732},{"emptyLinePlaceholder":3481},[10733],{"type":27,"value":3484},{"type":22,"tag":896,"props":10735,"children":10736},{"class":898,"line":1085},[10737,10742,10747],{"type":22,"tag":896,"props":10738,"children":10739},{"style":903},[10740],{"type":27,"value":10741},"mongod.",{"type":22,"tag":896,"props":10743,"children":10744},{"style":933},[10745],{"type":27,"value":10746},"stop",{"type":22,"tag":896,"props":10748,"children":10749},{"style":903},[10750],{"type":27,"value":4896},{"type":22,"tag":23,"props":10752,"children":10753},{},[10754,10756,10763,10765,10771],{"type":27,"value":10755},"In this case, ",{"type":22,"tag":30,"props":10757,"children":10760},{"href":10758,"rel":10759},"https://node-tap.org/",[34],[10761],{"type":27,"value":10762},"Node-Tap",{"type":27,"value":10764}," was used as the test runner, but any test runner will do. An annotated example of one of these tests is shown below, and the full set of changes (including additional test case examples) can be found ",{"type":22,"tag":30,"props":10766,"children":10769},{"href":10767,"rel":10768},"https://github.com/artandlogic/node-express-to-fastify-example/commit/e2a377060c842b7137e3f8e43bcc92734cb55500",[34],[10770],{"type":27,"value":4369},{"type":27,"value":71},{"type":22,"tag":23,"props":10773,"children":10774},{},[10775,10781],{"type":22,"tag":486,"props":10776,"children":10778},{"className":10777},[],[10779],{"type":27,"value":10780},"tests/users.test.js",{"type":27,"value":6801},{"type":22,"tag":886,"props":10783,"children":10785},{"className":2505,"code":10784,"language":2507,"meta":8,"style":8},"const {MongoMemoryServer} = require('mongodb-memory-server');\nconst {test} = require('tap');\n\nconst build = require('../app')\nconst {deleteAll} = require('../models');\nconst User = require('../models/User');\n\n// Add a test case for /users endpoints. In Node-Tap, test cases can be\n// nested arbitrarily.\ntest('/users', async t => {\n  let mongod, app;\n\n  t.before(async () => {\n    // Create a new in-memory MongoDB instance before the tests run,\n    // and build the Fastify application\n    mongod = await MongoMemoryServer.create();\n    process.env.NODE_ENV = 'test';\n    process.env.MONGODB_URI = mongod.getUri();\n    app = await build();\n  });\n  t.beforeEach(async t => {\n    // Before each test, call a utility which wipes all content from the\n    // database, so that test cases are fully independent.\n    await deleteAll();\n  });\n  t.teardown(async () => {\n    // Shut down the app and stop the database when tests complete.\n    await app.close();\n    await mongod.stop();\n  });\n\n  // Define a test for POST /users\n  await t.test('POST /users', async t => {\n    // Here is the Fastify inject() utility that's being leveraged for testing:\n    // A POST request is being simulated without a Webserver active, but it\n    // otherwise functions the same way that a true POST request would. The\n    // return value is a Promise that resolves to the Response object.\n    const response = await app.inject({\n      method: 'POST',\n      url: '/api/users',\n      headers: {\n        'Content-Type': 'application/json',\n        'X-Requested-With': '',\n      },\n      payload: {\n        user: {\n          email: 'john@jacob.com',\n          password: 'johnnyjacob',\n          username: 'johnjacob',\n        },\n      },\n    })\n\n    // With the Response in hand, whatever necessary test assertions can\n    // be made.\n    t.equal(response.statusCode, 200, 'returns a status code of 200');\n    const body = response.json();\n    t.hasProp(body, 'user', 'Response has \"user\" property\"');\n    t.hasProp(body.user, 'email', 'User has \"email\" property\"');\n    t.hasProp(body.user, 'username', 'User has \"username\" property\"');\n    t.hasProp(body.user, 'token', 'User has \"token\" property\"');\n  });\n});\n",[10786],{"type":22,"tag":486,"props":10787,"children":10788},{"__ignoreMap":8},[10789,10828,10868,10875,10907,10948,10981,10988,10996,11004,11041,11054,11061,11095,11103,11111,11139,11164,11191,11215,11222,11254,11262,11270,11287,11294,11326,11334,11354,11373,11380,11387,11395,11440,11448,11456,11464,11472,11506,11523,11540,11548,11569,11590,11598,11607,11616,11634,11652,11670,11678,11686,11695,11703,11712,11721,11758,11788,11824,11859,11893,11927,11935],{"type":22,"tag":896,"props":10790,"children":10791},{"class":898,"line":899},[10792,10796,10800,10804,10808,10812,10816,10820,10824],{"type":22,"tag":896,"props":10793,"children":10794},{"style":1691},[10795],{"type":27,"value":6169},{"type":22,"tag":896,"props":10797,"children":10798},{"style":903},[10799],{"type":27,"value":9006},{"type":22,"tag":896,"props":10801,"children":10802},{"style":1632},[10803],{"type":27,"value":10619},{"type":22,"tag":896,"props":10805,"children":10806},{"style":903},[10807],{"type":27,"value":5114},{"type":22,"tag":896,"props":10809,"children":10810},{"style":1691},[10811],{"type":27,"value":941},{"type":22,"tag":896,"props":10813,"children":10814},{"style":933},[10815],{"type":27,"value":4696},{"type":22,"tag":896,"props":10817,"children":10818},{"style":903},[10819],{"type":27,"value":2888},{"type":22,"tag":896,"props":10821,"children":10822},{"style":944},[10823],{"type":27,"value":10640},{"type":22,"tag":896,"props":10825,"children":10826},{"style":903},[10827],{"type":27,"value":3678},{"type":22,"tag":896,"props":10829,"children":10830},{"class":898,"line":758},[10831,10835,10839,10843,10847,10851,10855,10859,10864],{"type":22,"tag":896,"props":10832,"children":10833},{"style":1691},[10834],{"type":27,"value":6169},{"type":22,"tag":896,"props":10836,"children":10837},{"style":903},[10838],{"type":27,"value":9006},{"type":22,"tag":896,"props":10840,"children":10841},{"style":1632},[10842],{"type":27,"value":7286},{"type":22,"tag":896,"props":10844,"children":10845},{"style":903},[10846],{"type":27,"value":5114},{"type":22,"tag":896,"props":10848,"children":10849},{"style":1691},[10850],{"type":27,"value":941},{"type":22,"tag":896,"props":10852,"children":10853},{"style":933},[10854],{"type":27,"value":4696},{"type":22,"tag":896,"props":10856,"children":10857},{"style":903},[10858],{"type":27,"value":2888},{"type":22,"tag":896,"props":10860,"children":10861},{"style":944},[10862],{"type":27,"value":10863},"'tap'",{"type":22,"tag":896,"props":10865,"children":10866},{"style":903},[10867],{"type":27,"value":3678},{"type":22,"tag":896,"props":10869,"children":10870},{"class":898,"line":755},[10871],{"type":22,"tag":896,"props":10872,"children":10873},{"emptyLinePlaceholder":3481},[10874],{"type":27,"value":3484},{"type":22,"tag":896,"props":10876,"children":10877},{"class":898,"line":983},[10878,10882,10886,10890,10894,10898,10903],{"type":22,"tag":896,"props":10879,"children":10880},{"style":1691},[10881],{"type":27,"value":6169},{"type":22,"tag":896,"props":10883,"children":10884},{"style":1632},[10885],{"type":27,"value":5571},{"type":22,"tag":896,"props":10887,"children":10888},{"style":1691},[10889],{"type":27,"value":3638},{"type":22,"tag":896,"props":10891,"children":10892},{"style":933},[10893],{"type":27,"value":4696},{"type":22,"tag":896,"props":10895,"children":10896},{"style":903},[10897],{"type":27,"value":2888},{"type":22,"tag":896,"props":10899,"children":10900},{"style":944},[10901],{"type":27,"value":10902},"'../app'",{"type":22,"tag":896,"props":10904,"children":10905},{"style":903},[10906],{"type":27,"value":2903},{"type":22,"tag":896,"props":10908,"children":10909},{"class":898,"line":1013},[10910,10914,10918,10923,10927,10931,10935,10939,10944],{"type":22,"tag":896,"props":10911,"children":10912},{"style":1691},[10913],{"type":27,"value":6169},{"type":22,"tag":896,"props":10915,"children":10916},{"style":903},[10917],{"type":27,"value":9006},{"type":22,"tag":896,"props":10919,"children":10920},{"style":1632},[10921],{"type":27,"value":10922},"deleteAll",{"type":22,"tag":896,"props":10924,"children":10925},{"style":903},[10926],{"type":27,"value":5114},{"type":22,"tag":896,"props":10928,"children":10929},{"style":1691},[10930],{"type":27,"value":941},{"type":22,"tag":896,"props":10932,"children":10933},{"style":933},[10934],{"type":27,"value":4696},{"type":22,"tag":896,"props":10936,"children":10937},{"style":903},[10938],{"type":27,"value":2888},{"type":22,"tag":896,"props":10940,"children":10941},{"style":944},[10942],{"type":27,"value":10943},"'../models'",{"type":22,"tag":896,"props":10945,"children":10946},{"style":903},[10947],{"type":27,"value":3678},{"type":22,"tag":896,"props":10949,"children":10950},{"class":898,"line":1030},[10951,10955,10960,10964,10968,10972,10977],{"type":22,"tag":896,"props":10952,"children":10953},{"style":1691},[10954],{"type":27,"value":6169},{"type":22,"tag":896,"props":10956,"children":10957},{"style":1632},[10958],{"type":27,"value":10959}," User",{"type":22,"tag":896,"props":10961,"children":10962},{"style":1691},[10963],{"type":27,"value":3638},{"type":22,"tag":896,"props":10965,"children":10966},{"style":933},[10967],{"type":27,"value":4696},{"type":22,"tag":896,"props":10969,"children":10970},{"style":903},[10971],{"type":27,"value":2888},{"type":22,"tag":896,"props":10973,"children":10974},{"style":944},[10975],{"type":27,"value":10976},"'../models/User'",{"type":22,"tag":896,"props":10978,"children":10979},{"style":903},[10980],{"type":27,"value":3678},{"type":22,"tag":896,"props":10982,"children":10983},{"class":898,"line":1068},[10984],{"type":22,"tag":896,"props":10985,"children":10986},{"emptyLinePlaceholder":3481},[10987],{"type":27,"value":3484},{"type":22,"tag":896,"props":10989,"children":10990},{"class":898,"line":1085},[10991],{"type":22,"tag":896,"props":10992,"children":10993},{"style":2070},[10994],{"type":27,"value":10995},"// Add a test case for /users endpoints. In Node-Tap, test cases can be\n",{"type":22,"tag":896,"props":10997,"children":10998},{"class":898,"line":1123},[10999],{"type":22,"tag":896,"props":11000,"children":11001},{"style":2070},[11002],{"type":27,"value":11003},"// nested arbitrarily.\n",{"type":22,"tag":896,"props":11005,"children":11006},{"class":898,"line":1161},[11007,11011,11015,11020,11024,11028,11033,11037],{"type":22,"tag":896,"props":11008,"children":11009},{"style":933},[11010],{"type":27,"value":7286},{"type":22,"tag":896,"props":11012,"children":11013},{"style":903},[11014],{"type":27,"value":2888},{"type":22,"tag":896,"props":11016,"children":11017},{"style":944},[11018],{"type":27,"value":11019},"'/users'",{"type":22,"tag":896,"props":11021,"children":11022},{"style":903},[11023],{"type":27,"value":236},{"type":22,"tag":896,"props":11025,"children":11026},{"style":1691},[11027],{"type":27,"value":5561},{"type":22,"tag":896,"props":11029,"children":11030},{"style":6025},[11031],{"type":27,"value":11032}," t",{"type":22,"tag":896,"props":11034,"children":11035},{"style":1691},[11036],{"type":27,"value":6033},{"type":22,"tag":896,"props":11038,"children":11039},{"style":903},[11040],{"type":27,"value":1626},{"type":22,"tag":896,"props":11042,"children":11043},{"class":898,"line":2298},[11044,11049],{"type":22,"tag":896,"props":11045,"children":11046},{"style":1691},[11047],{"type":27,"value":11048},"  let",{"type":22,"tag":896,"props":11050,"children":11051},{"style":903},[11052],{"type":27,"value":11053}," mongod, app;\n",{"type":22,"tag":896,"props":11055,"children":11056},{"class":898,"line":3170},[11057],{"type":22,"tag":896,"props":11058,"children":11059},{"emptyLinePlaceholder":3481},[11060],{"type":27,"value":3484},{"type":22,"tag":896,"props":11062,"children":11063},{"class":898,"line":3187},[11064,11069,11074,11078,11082,11087,11091],{"type":22,"tag":896,"props":11065,"children":11066},{"style":903},[11067],{"type":27,"value":11068},"  t.",{"type":22,"tag":896,"props":11070,"children":11071},{"style":933},[11072],{"type":27,"value":11073},"before",{"type":22,"tag":896,"props":11075,"children":11076},{"style":903},[11077],{"type":27,"value":2888},{"type":22,"tag":896,"props":11079,"children":11080},{"style":1691},[11081],{"type":27,"value":5561},{"type":22,"tag":896,"props":11083,"children":11084},{"style":903},[11085],{"type":27,"value":11086}," () ",{"type":22,"tag":896,"props":11088,"children":11089},{"style":1691},[11090],{"type":27,"value":6087},{"type":22,"tag":896,"props":11092,"children":11093},{"style":903},[11094],{"type":27,"value":1626},{"type":22,"tag":896,"props":11096,"children":11097},{"class":898,"line":3228},[11098],{"type":22,"tag":896,"props":11099,"children":11100},{"style":2070},[11101],{"type":27,"value":11102},"    // Create a new in-memory MongoDB instance before the tests run,\n",{"type":22,"tag":896,"props":11104,"children":11105},{"class":898,"line":3245},[11106],{"type":22,"tag":896,"props":11107,"children":11108},{"style":2070},[11109],{"type":27,"value":11110},"    // and build the Fastify application\n",{"type":22,"tag":896,"props":11112,"children":11113},{"class":898,"line":3286},[11114,11119,11123,11127,11131,11135],{"type":22,"tag":896,"props":11115,"children":11116},{"style":903},[11117],{"type":27,"value":11118},"    mongod ",{"type":22,"tag":896,"props":11120,"children":11121},{"style":1691},[11122],{"type":27,"value":941},{"type":22,"tag":896,"props":11124,"children":11125},{"style":1691},[11126],{"type":27,"value":9998},{"type":22,"tag":896,"props":11128,"children":11129},{"style":903},[11130],{"type":27,"value":10672},{"type":22,"tag":896,"props":11132,"children":11133},{"style":933},[11134],{"type":27,"value":10677},{"type":22,"tag":896,"props":11136,"children":11137},{"style":903},[11138],{"type":27,"value":4896},{"type":22,"tag":896,"props":11140,"children":11141},{"class":898,"line":3303},[11142,11147,11151,11155,11160],{"type":22,"tag":896,"props":11143,"children":11144},{"style":903},[11145],{"type":27,"value":11146},"    process.env.",{"type":22,"tag":896,"props":11148,"children":11149},{"style":1632},[11150],{"type":27,"value":4849},{"type":22,"tag":896,"props":11152,"children":11153},{"style":1691},[11154],{"type":27,"value":3638},{"type":22,"tag":896,"props":11156,"children":11157},{"style":944},[11158],{"type":27,"value":11159}," 'test'",{"type":22,"tag":896,"props":11161,"children":11162},{"style":903},[11163],{"type":27,"value":1649},{"type":22,"tag":896,"props":11165,"children":11166},{"class":898,"line":3344},[11167,11171,11175,11179,11183,11187],{"type":22,"tag":896,"props":11168,"children":11169},{"style":903},[11170],{"type":27,"value":11146},{"type":22,"tag":896,"props":11172,"children":11173},{"style":1632},[11174],{"type":27,"value":5102},{"type":22,"tag":896,"props":11176,"children":11177},{"style":1691},[11178],{"type":27,"value":3638},{"type":22,"tag":896,"props":11180,"children":11181},{"style":903},[11182],{"type":27,"value":10702},{"type":22,"tag":896,"props":11184,"children":11185},{"style":933},[11186],{"type":27,"value":10707},{"type":22,"tag":896,"props":11188,"children":11189},{"style":903},[11190],{"type":27,"value":4896},{"type":22,"tag":896,"props":11192,"children":11193},{"class":898,"line":3361},[11194,11199,11203,11207,11211],{"type":22,"tag":896,"props":11195,"children":11196},{"style":903},[11197],{"type":27,"value":11198},"    app ",{"type":22,"tag":896,"props":11200,"children":11201},{"style":1691},[11202],{"type":27,"value":941},{"type":22,"tag":896,"props":11204,"children":11205},{"style":1691},[11206],{"type":27,"value":9998},{"type":22,"tag":896,"props":11208,"children":11209},{"style":933},[11210],{"type":27,"value":5571},{"type":22,"tag":896,"props":11212,"children":11213},{"style":903},[11214],{"type":27,"value":4896},{"type":22,"tag":896,"props":11216,"children":11217},{"class":898,"line":3402},[11218],{"type":22,"tag":896,"props":11219,"children":11220},{"style":903},[11221],{"type":27,"value":5887},{"type":22,"tag":896,"props":11223,"children":11224},{"class":898,"line":3419},[11225,11229,11234,11238,11242,11246,11250],{"type":22,"tag":896,"props":11226,"children":11227},{"style":903},[11228],{"type":27,"value":11068},{"type":22,"tag":896,"props":11230,"children":11231},{"style":933},[11232],{"type":27,"value":11233},"beforeEach",{"type":22,"tag":896,"props":11235,"children":11236},{"style":903},[11237],{"type":27,"value":2888},{"type":22,"tag":896,"props":11239,"children":11240},{"style":1691},[11241],{"type":27,"value":5561},{"type":22,"tag":896,"props":11243,"children":11244},{"style":6025},[11245],{"type":27,"value":11032},{"type":22,"tag":896,"props":11247,"children":11248},{"style":1691},[11249],{"type":27,"value":6033},{"type":22,"tag":896,"props":11251,"children":11252},{"style":903},[11253],{"type":27,"value":1626},{"type":22,"tag":896,"props":11255,"children":11256},{"class":898,"line":3460},[11257],{"type":22,"tag":896,"props":11258,"children":11259},{"style":2070},[11260],{"type":27,"value":11261},"    // Before each test, call a utility which wipes all content from the\n",{"type":22,"tag":896,"props":11263,"children":11264},{"class":898,"line":3477},[11265],{"type":22,"tag":896,"props":11266,"children":11267},{"style":2070},[11268],{"type":27,"value":11269},"    // database, so that test cases are fully independent.\n",{"type":22,"tag":896,"props":11271,"children":11272},{"class":898,"line":3487},[11273,11278,11283],{"type":22,"tag":896,"props":11274,"children":11275},{"style":1691},[11276],{"type":27,"value":11277},"    await",{"type":22,"tag":896,"props":11279,"children":11280},{"style":933},[11281],{"type":27,"value":11282}," deleteAll",{"type":22,"tag":896,"props":11284,"children":11285},{"style":903},[11286],{"type":27,"value":4896},{"type":22,"tag":896,"props":11288,"children":11289},{"class":898,"line":3505},[11290],{"type":22,"tag":896,"props":11291,"children":11292},{"style":903},[11293],{"type":27,"value":5887},{"type":22,"tag":896,"props":11295,"children":11296},{"class":898,"line":5985},[11297,11301,11306,11310,11314,11318,11322],{"type":22,"tag":896,"props":11298,"children":11299},{"style":903},[11300],{"type":27,"value":11068},{"type":22,"tag":896,"props":11302,"children":11303},{"style":933},[11304],{"type":27,"value":11305},"teardown",{"type":22,"tag":896,"props":11307,"children":11308},{"style":903},[11309],{"type":27,"value":2888},{"type":22,"tag":896,"props":11311,"children":11312},{"style":1691},[11313],{"type":27,"value":5561},{"type":22,"tag":896,"props":11315,"children":11316},{"style":903},[11317],{"type":27,"value":11086},{"type":22,"tag":896,"props":11319,"children":11320},{"style":1691},[11321],{"type":27,"value":6087},{"type":22,"tag":896,"props":11323,"children":11324},{"style":903},[11325],{"type":27,"value":1626},{"type":22,"tag":896,"props":11327,"children":11328},{"class":898,"line":5993},[11329],{"type":22,"tag":896,"props":11330,"children":11331},{"style":2070},[11332],{"type":27,"value":11333},"    // Shut down the app and stop the database when tests complete.\n",{"type":22,"tag":896,"props":11335,"children":11336},{"class":898,"line":6007},[11337,11341,11345,11350],{"type":22,"tag":896,"props":11338,"children":11339},{"style":1691},[11340],{"type":27,"value":11277},{"type":22,"tag":896,"props":11342,"children":11343},{"style":903},[11344],{"type":27,"value":5361},{"type":22,"tag":896,"props":11346,"children":11347},{"style":933},[11348],{"type":27,"value":11349},"close",{"type":22,"tag":896,"props":11351,"children":11352},{"style":903},[11353],{"type":27,"value":4896},{"type":22,"tag":896,"props":11355,"children":11356},{"class":898,"line":6059},[11357,11361,11365,11369],{"type":22,"tag":896,"props":11358,"children":11359},{"style":1691},[11360],{"type":27,"value":11277},{"type":22,"tag":896,"props":11362,"children":11363},{"style":903},[11364],{"type":27,"value":10702},{"type":22,"tag":896,"props":11366,"children":11367},{"style":933},[11368],{"type":27,"value":10746},{"type":22,"tag":896,"props":11370,"children":11371},{"style":903},[11372],{"type":27,"value":4896},{"type":22,"tag":896,"props":11374,"children":11375},{"class":898,"line":6094},[11376],{"type":22,"tag":896,"props":11377,"children":11378},{"style":903},[11379],{"type":27,"value":5887},{"type":22,"tag":896,"props":11381,"children":11382},{"class":898,"line":6125},[11383],{"type":22,"tag":896,"props":11384,"children":11385},{"emptyLinePlaceholder":3481},[11386],{"type":27,"value":3484},{"type":22,"tag":896,"props":11388,"children":11389},{"class":898,"line":7440},[11390],{"type":22,"tag":896,"props":11391,"children":11392},{"style":2070},[11393],{"type":27,"value":11394},"  // Define a test for POST /users\n",{"type":22,"tag":896,"props":11396,"children":11397},{"class":898,"line":7463},[11398,11402,11407,11411,11415,11420,11424,11428,11432,11436],{"type":22,"tag":896,"props":11399,"children":11400},{"style":1691},[11401],{"type":27,"value":5895},{"type":22,"tag":896,"props":11403,"children":11404},{"style":903},[11405],{"type":27,"value":11406}," t.",{"type":22,"tag":896,"props":11408,"children":11409},{"style":933},[11410],{"type":27,"value":7286},{"type":22,"tag":896,"props":11412,"children":11413},{"style":903},[11414],{"type":27,"value":2888},{"type":22,"tag":896,"props":11416,"children":11417},{"style":944},[11418],{"type":27,"value":11419},"'POST /users'",{"type":22,"tag":896,"props":11421,"children":11422},{"style":903},[11423],{"type":27,"value":236},{"type":22,"tag":896,"props":11425,"children":11426},{"style":1691},[11427],{"type":27,"value":5561},{"type":22,"tag":896,"props":11429,"children":11430},{"style":6025},[11431],{"type":27,"value":11032},{"type":22,"tag":896,"props":11433,"children":11434},{"style":1691},[11435],{"type":27,"value":6033},{"type":22,"tag":896,"props":11437,"children":11438},{"style":903},[11439],{"type":27,"value":1626},{"type":22,"tag":896,"props":11441,"children":11442},{"class":898,"line":7481},[11443],{"type":22,"tag":896,"props":11444,"children":11445},{"style":2070},[11446],{"type":27,"value":11447},"    // Here is the Fastify inject() utility that's being leveraged for testing:\n",{"type":22,"tag":896,"props":11449,"children":11450},{"class":898,"line":7500},[11451],{"type":22,"tag":896,"props":11452,"children":11453},{"style":2070},[11454],{"type":27,"value":11455},"    // A POST request is being simulated without a Webserver active, but it\n",{"type":22,"tag":896,"props":11457,"children":11458},{"class":898,"line":7509},[11459],{"type":22,"tag":896,"props":11460,"children":11461},{"style":2070},[11462],{"type":27,"value":11463},"    // otherwise functions the same way that a true POST request would. The\n",{"type":22,"tag":896,"props":11465,"children":11466},{"class":898,"line":7517},[11467],{"type":22,"tag":896,"props":11468,"children":11469},{"style":2070},[11470],{"type":27,"value":11471},"    // return value is a Promise that resolves to the Response object.\n",{"type":22,"tag":896,"props":11473,"children":11474},{"class":898,"line":7526},[11475,11480,11485,11489,11493,11497,11502],{"type":22,"tag":896,"props":11476,"children":11477},{"style":1691},[11478],{"type":27,"value":11479},"    const",{"type":22,"tag":896,"props":11481,"children":11482},{"style":1632},[11483],{"type":27,"value":11484}," response",{"type":22,"tag":896,"props":11486,"children":11487},{"style":1691},[11488],{"type":27,"value":3638},{"type":22,"tag":896,"props":11490,"children":11491},{"style":1691},[11492],{"type":27,"value":9998},{"type":22,"tag":896,"props":11494,"children":11495},{"style":903},[11496],{"type":27,"value":5361},{"type":22,"tag":896,"props":11498,"children":11499},{"style":933},[11500],{"type":27,"value":11501},"inject",{"type":22,"tag":896,"props":11503,"children":11504},{"style":903},[11505],{"type":27,"value":8574},{"type":22,"tag":896,"props":11507,"children":11508},{"class":898,"line":7583},[11509,11514,11519],{"type":22,"tag":896,"props":11510,"children":11511},{"style":903},[11512],{"type":27,"value":11513},"      method: ",{"type":22,"tag":896,"props":11515,"children":11516},{"style":944},[11517],{"type":27,"value":11518},"'POST'",{"type":22,"tag":896,"props":11520,"children":11521},{"style":903},[11522],{"type":27,"value":3988},{"type":22,"tag":896,"props":11524,"children":11525},{"class":898,"line":7595},[11526,11531,11536],{"type":22,"tag":896,"props":11527,"children":11528},{"style":903},[11529],{"type":27,"value":11530},"      url: ",{"type":22,"tag":896,"props":11532,"children":11533},{"style":944},[11534],{"type":27,"value":11535},"'/api/users'",{"type":22,"tag":896,"props":11537,"children":11538},{"style":903},[11539],{"type":27,"value":3988},{"type":22,"tag":896,"props":11541,"children":11542},{"class":898,"line":7615},[11543],{"type":22,"tag":896,"props":11544,"children":11545},{"style":903},[11546],{"type":27,"value":11547},"      headers: {\n",{"type":22,"tag":896,"props":11549,"children":11550},{"class":898,"line":7632},[11551,11556,11560,11565],{"type":22,"tag":896,"props":11552,"children":11553},{"style":944},[11554],{"type":27,"value":11555},"        'Content-Type'",{"type":22,"tag":896,"props":11557,"children":11558},{"style":903},[11559],{"type":27,"value":1640},{"type":22,"tag":896,"props":11561,"children":11562},{"style":944},[11563],{"type":27,"value":11564},"'application/json'",{"type":22,"tag":896,"props":11566,"children":11567},{"style":903},[11568],{"type":27,"value":3988},{"type":22,"tag":896,"props":11570,"children":11571},{"class":898,"line":7640},[11572,11577,11581,11586],{"type":22,"tag":896,"props":11573,"children":11574},{"style":944},[11575],{"type":27,"value":11576},"        'X-Requested-With'",{"type":22,"tag":896,"props":11578,"children":11579},{"style":903},[11580],{"type":27,"value":1640},{"type":22,"tag":896,"props":11582,"children":11583},{"style":944},[11584],{"type":27,"value":11585},"''",{"type":22,"tag":896,"props":11587,"children":11588},{"style":903},[11589],{"type":27,"value":3988},{"type":22,"tag":896,"props":11591,"children":11593},{"class":898,"line":11592},44,[11594],{"type":22,"tag":896,"props":11595,"children":11596},{"style":903},[11597],{"type":27,"value":7344},{"type":22,"tag":896,"props":11599,"children":11601},{"class":898,"line":11600},45,[11602],{"type":22,"tag":896,"props":11603,"children":11604},{"style":903},[11605],{"type":27,"value":11606},"      payload: {\n",{"type":22,"tag":896,"props":11608,"children":11610},{"class":898,"line":11609},46,[11611],{"type":22,"tag":896,"props":11612,"children":11613},{"style":903},[11614],{"type":27,"value":11615},"        user: {\n",{"type":22,"tag":896,"props":11617,"children":11619},{"class":898,"line":11618},47,[11620,11625,11630],{"type":22,"tag":896,"props":11621,"children":11622},{"style":903},[11623],{"type":27,"value":11624},"          email: ",{"type":22,"tag":896,"props":11626,"children":11627},{"style":944},[11628],{"type":27,"value":11629},"'john@jacob.com'",{"type":22,"tag":896,"props":11631,"children":11632},{"style":903},[11633],{"type":27,"value":3988},{"type":22,"tag":896,"props":11635,"children":11637},{"class":898,"line":11636},48,[11638,11643,11648],{"type":22,"tag":896,"props":11639,"children":11640},{"style":903},[11641],{"type":27,"value":11642},"          password: ",{"type":22,"tag":896,"props":11644,"children":11645},{"style":944},[11646],{"type":27,"value":11647},"'johnnyjacob'",{"type":22,"tag":896,"props":11649,"children":11650},{"style":903},[11651],{"type":27,"value":3988},{"type":22,"tag":896,"props":11653,"children":11655},{"class":898,"line":11654},49,[11656,11661,11666],{"type":22,"tag":896,"props":11657,"children":11658},{"style":903},[11659],{"type":27,"value":11660},"          username: ",{"type":22,"tag":896,"props":11662,"children":11663},{"style":944},[11664],{"type":27,"value":11665},"'johnjacob'",{"type":22,"tag":896,"props":11667,"children":11668},{"style":903},[11669],{"type":27,"value":3988},{"type":22,"tag":896,"props":11671,"children":11673},{"class":898,"line":11672},50,[11674],{"type":22,"tag":896,"props":11675,"children":11676},{"style":903},[11677],{"type":27,"value":9448},{"type":22,"tag":896,"props":11679,"children":11681},{"class":898,"line":11680},51,[11682],{"type":22,"tag":896,"props":11683,"children":11684},{"style":903},[11685],{"type":27,"value":7344},{"type":22,"tag":896,"props":11687,"children":11689},{"class":898,"line":11688},52,[11690],{"type":22,"tag":896,"props":11691,"children":11692},{"style":903},[11693],{"type":27,"value":11694},"    })\n",{"type":22,"tag":896,"props":11696,"children":11698},{"class":898,"line":11697},53,[11699],{"type":22,"tag":896,"props":11700,"children":11701},{"emptyLinePlaceholder":3481},[11702],{"type":27,"value":3484},{"type":22,"tag":896,"props":11704,"children":11706},{"class":898,"line":11705},54,[11707],{"type":22,"tag":896,"props":11708,"children":11709},{"style":2070},[11710],{"type":27,"value":11711},"    // With the Response in hand, whatever necessary test assertions can\n",{"type":22,"tag":896,"props":11713,"children":11715},{"class":898,"line":11714},55,[11716],{"type":22,"tag":896,"props":11717,"children":11718},{"style":2070},[11719],{"type":27,"value":11720},"    // be made.\n",{"type":22,"tag":896,"props":11722,"children":11724},{"class":898,"line":11723},56,[11725,11730,11735,11740,11745,11749,11754],{"type":22,"tag":896,"props":11726,"children":11727},{"style":903},[11728],{"type":27,"value":11729},"    t.",{"type":22,"tag":896,"props":11731,"children":11732},{"style":933},[11733],{"type":27,"value":11734},"equal",{"type":22,"tag":896,"props":11736,"children":11737},{"style":903},[11738],{"type":27,"value":11739},"(response.statusCode, ",{"type":22,"tag":896,"props":11741,"children":11742},{"style":1632},[11743],{"type":27,"value":11744},"200",{"type":22,"tag":896,"props":11746,"children":11747},{"style":903},[11748],{"type":27,"value":236},{"type":22,"tag":896,"props":11750,"children":11751},{"style":944},[11752],{"type":27,"value":11753},"'returns a status code of 200'",{"type":22,"tag":896,"props":11755,"children":11756},{"style":903},[11757],{"type":27,"value":3678},{"type":22,"tag":896,"props":11759,"children":11761},{"class":898,"line":11760},57,[11762,11766,11771,11775,11780,11784],{"type":22,"tag":896,"props":11763,"children":11764},{"style":1691},[11765],{"type":27,"value":11479},{"type":22,"tag":896,"props":11767,"children":11768},{"style":1632},[11769],{"type":27,"value":11770}," body",{"type":22,"tag":896,"props":11772,"children":11773},{"style":1691},[11774],{"type":27,"value":3638},{"type":22,"tag":896,"props":11776,"children":11777},{"style":903},[11778],{"type":27,"value":11779}," response.",{"type":22,"tag":896,"props":11781,"children":11782},{"style":933},[11783],{"type":27,"value":5042},{"type":22,"tag":896,"props":11785,"children":11786},{"style":903},[11787],{"type":27,"value":4896},{"type":22,"tag":896,"props":11789,"children":11791},{"class":898,"line":11790},58,[11792,11796,11801,11806,11811,11815,11820],{"type":22,"tag":896,"props":11793,"children":11794},{"style":903},[11795],{"type":27,"value":11729},{"type":22,"tag":896,"props":11797,"children":11798},{"style":933},[11799],{"type":27,"value":11800},"hasProp",{"type":22,"tag":896,"props":11802,"children":11803},{"style":903},[11804],{"type":27,"value":11805},"(body, ",{"type":22,"tag":896,"props":11807,"children":11808},{"style":944},[11809],{"type":27,"value":11810},"'user'",{"type":22,"tag":896,"props":11812,"children":11813},{"style":903},[11814],{"type":27,"value":236},{"type":22,"tag":896,"props":11816,"children":11817},{"style":944},[11818],{"type":27,"value":11819},"'Response has \"user\" property\"'",{"type":22,"tag":896,"props":11821,"children":11822},{"style":903},[11823],{"type":27,"value":3678},{"type":22,"tag":896,"props":11825,"children":11827},{"class":898,"line":11826},59,[11828,11832,11836,11841,11846,11850,11855],{"type":22,"tag":896,"props":11829,"children":11830},{"style":903},[11831],{"type":27,"value":11729},{"type":22,"tag":896,"props":11833,"children":11834},{"style":933},[11835],{"type":27,"value":11800},{"type":22,"tag":896,"props":11837,"children":11838},{"style":903},[11839],{"type":27,"value":11840},"(body.user, ",{"type":22,"tag":896,"props":11842,"children":11843},{"style":944},[11844],{"type":27,"value":11845},"'email'",{"type":22,"tag":896,"props":11847,"children":11848},{"style":903},[11849],{"type":27,"value":236},{"type":22,"tag":896,"props":11851,"children":11852},{"style":944},[11853],{"type":27,"value":11854},"'User has \"email\" property\"'",{"type":22,"tag":896,"props":11856,"children":11857},{"style":903},[11858],{"type":27,"value":3678},{"type":22,"tag":896,"props":11860,"children":11862},{"class":898,"line":11861},60,[11863,11867,11871,11875,11880,11884,11889],{"type":22,"tag":896,"props":11864,"children":11865},{"style":903},[11866],{"type":27,"value":11729},{"type":22,"tag":896,"props":11868,"children":11869},{"style":933},[11870],{"type":27,"value":11800},{"type":22,"tag":896,"props":11872,"children":11873},{"style":903},[11874],{"type":27,"value":11840},{"type":22,"tag":896,"props":11876,"children":11877},{"style":944},[11878],{"type":27,"value":11879},"'username'",{"type":22,"tag":896,"props":11881,"children":11882},{"style":903},[11883],{"type":27,"value":236},{"type":22,"tag":896,"props":11885,"children":11886},{"style":944},[11887],{"type":27,"value":11888},"'User has \"username\" property\"'",{"type":22,"tag":896,"props":11890,"children":11891},{"style":903},[11892],{"type":27,"value":3678},{"type":22,"tag":896,"props":11894,"children":11896},{"class":898,"line":11895},61,[11897,11901,11905,11909,11914,11918,11923],{"type":22,"tag":896,"props":11898,"children":11899},{"style":903},[11900],{"type":27,"value":11729},{"type":22,"tag":896,"props":11902,"children":11903},{"style":933},[11904],{"type":27,"value":11800},{"type":22,"tag":896,"props":11906,"children":11907},{"style":903},[11908],{"type":27,"value":11840},{"type":22,"tag":896,"props":11910,"children":11911},{"style":944},[11912],{"type":27,"value":11913},"'token'",{"type":22,"tag":896,"props":11915,"children":11916},{"style":903},[11917],{"type":27,"value":236},{"type":22,"tag":896,"props":11919,"children":11920},{"style":944},[11921],{"type":27,"value":11922},"'User has \"token\" property\"'",{"type":22,"tag":896,"props":11924,"children":11925},{"style":903},[11926],{"type":27,"value":3678},{"type":22,"tag":896,"props":11928,"children":11930},{"class":898,"line":11929},62,[11931],{"type":22,"tag":896,"props":11932,"children":11933},{"style":903},[11934],{"type":27,"value":5887},{"type":22,"tag":896,"props":11936,"children":11938},{"class":898,"line":11937},63,[11939],{"type":22,"tag":896,"props":11940,"children":11941},{"style":903},[11942],{"type":27,"value":5450},{"type":22,"tag":193,"props":11944,"children":11946},{"id":11945},"conclusions",[11947],{"type":27,"value":11948},"Conclusions",{"type":22,"tag":23,"props":11950,"children":11951},{},[11952,11954,11959],{"type":27,"value":11953},"Completing this migration took me several hours, some of which was ramping up on ",{"type":22,"tag":486,"props":11955,"children":11957},{"className":11956},[],[11958],{"type":27,"value":4393},{"type":27,"value":11960}," and making changes to the app structure, but a lot of which was working through each migrated API endpoint. Consequently, I think the time it takes to complete a migration is going to scale linearly with the size of a project.",{"type":22,"tag":23,"props":11962,"children":11963},{},[11964],{"type":27,"value":11965},"While I do think Fastify is a better choice than Express as a lightweight Web framework for Node.js, the cost of migrating relative to the benefits makes it such that I would only recommend doing it in specific scenarios, for example for small or early-stage projects, or when a major refactor is already going to take place. That said, I found the process itself to be smooth and reliable, and with good pre-existing test coverage I feel it's about as safe to do as any major refactor can be.",{"type":22,"tag":23,"props":11967,"children":11968},{},[11969,11971,11976],{"type":27,"value":11970},"I hope this example is helpful for anyone considering making the switch to Fastify; the full source code is available on GitHub ",{"type":22,"tag":30,"props":11972,"children":11974},{"href":4365,"rel":11973},[34],[11975],{"type":27,"value":4369},{"type":27,"value":71},{"type":22,"tag":1999,"props":11978,"children":11979},{},[11980],{"type":27,"value":2457},{"title":8,"searchDepth":755,"depth":755,"links":11982},[11983,11984,11985,11986,11991,11992,11993],{"id":4374,"depth":758,"text":4377},{"id":4435,"depth":758,"text":4438},{"id":4568,"depth":758,"text":4571},{"id":6743,"depth":758,"text":6746,"children":11987},[11988,11989,11990],{"id":6749,"depth":755,"text":6752},{"id":7819,"depth":755,"text":7822},{"id":8375,"depth":755,"text":8378},{"id":10156,"depth":758,"text":10159},{"id":10216,"depth":758,"text":10219},{"id":11945,"depth":758,"text":11948},"content:phendry:2022-07-28:MigratingFromExpressToFastifyPart2.md","phendry/2022-07-28/MigratingFromExpressToFastifyPart2.md","phendry/2022-07-28/MigratingFromExpressToFastifyPart2",{"user":774,"name":775},{"_path":11999,"_dir":12000,"_draft":7,"_partial":7,"_locale":8,"title":12001,"description":12002,"image":12003,"tags":12004,"publishDate":12005,"excerpt":12002,"body":12006,"_type":767,"_id":12476,"_source":769,"_file":12477,"_stem":12478,"_extension":772,"author":12479},"/phendry/2022-07-21/migratingfromexpresstofastifypart1","2022-07-21","Migrating from Express to Fastify, Part 1","Express.js has for years been the dominant lightweight Web framework for Node.js, but over time its development has stalled, with its latest major version (5.0) still in pre-release nearly eight years after its first alpha release. There's a lot to be said for this sort of stability in a foundational dependency for a project, but it's worth assessing whether the added features of competing frameworks are worth making a switch. In this article we'll be looking at Fastify in particular, to understand what it has to offer compared to Express and how difficult it is to migrate an existing Express project.","/phendry/2022-07-21/img/Migrating from Express to Fastify, Part 1.png",[2507,2819],"2023-12-01",{"type":19,"children":12007,"toc":12462},[12008,12025,12031,12036,12041,12047,12060,12066,12080,12100,12149,12155,12186,12192,12206,12212,12217,12242,12247,12269,12275,12280,12316,12322,12354,12359,12398,12404,12409,12414,12430,12442],{"type":22,"tag":23,"props":12009,"children":12010},{},[12011,12016,12018,12023],{"type":22,"tag":30,"props":12012,"children":12014},{"href":4324,"rel":12013},[34],[12015],{"type":27,"value":4328},{"type":27,"value":12017}," has for years been the dominant lightweight Web framework for Node.js, but over time its development has stalled, with its latest major version (5.0) still in pre-release nearly eight years after its first alpha release. There's a lot to be said for this sort of stability in a foundational dependency for a project, but it's worth assessing whether the added features of competing frameworks are worth making a switch. In this article we'll be looking at ",{"type":22,"tag":30,"props":12019,"children":12021},{"href":4315,"rel":12020},[34],[12022],{"type":27,"value":4319},{"type":27,"value":12024}," in particular, to understand what it has to offer compared to Express and how difficult it is to migrate an existing Express project.",{"type":22,"tag":193,"props":12026,"children":12028},{"id":12027},"why-fastify",[12029],{"type":27,"value":12030},"Why Fastify?",{"type":22,"tag":23,"props":12032,"children":12033},{},[12034],{"type":27,"value":12035},"Like Express, Fastify is lightweight and unopinionated, it's very popular (which bodes well for the longevity of the project), it has good documentation, and it provides comparable features (routing, templating, middleware, etc).",{"type":22,"tag":23,"props":12037,"children":12038},{},[12039],{"type":27,"value":12040},"It has considerably more to offer, however:",{"type":22,"tag":1481,"props":12042,"children":12044},{"id":12043},"a-committed-long-term-support-schedule",[12045],{"type":27,"value":12046},"A committed long-term support schedule",{"type":22,"tag":23,"props":12048,"children":12049},{},[12050,12051,12058],{"type":27,"value":10237},{"type":22,"tag":30,"props":12052,"children":12055},{"href":12053,"rel":12054},"https://www.fastify.io/docs/latest/Reference/LTS/",[34],[12056],{"type":27,"value":12057},"LTS documentation",{"type":27,"value":12059}," makes it easy to see the end-of-life date and supported Node versions of a given release. For a foundational dependency like a Web framework, upgrades have the potential to be time-consuming and error-prone, so being able to do so on a predictable schedule is key.",{"type":22,"tag":1481,"props":12061,"children":12063},{"id":12062},"quality-major-version-upgrade-guides",[12064],{"type":27,"value":12065},"Quality major version upgrade guides",{"type":22,"tag":23,"props":12067,"children":12068},{},[12069,12071,12078],{"type":27,"value":12070},"The project has a history of providing comprehensive ",{"type":22,"tag":30,"props":12072,"children":12075},{"href":12073,"rel":12074},"https://www.fastify.io/docs/latest/Guides/Migration-Guide-V4/",[34],[12076],{"type":27,"value":12077},"migration guides",{"type":27,"value":12079},", which provides a lot of reassurance that future upgrades can be done smoothly, systematically and without introducing bugs.",{"type":22,"tag":1481,"props":12081,"children":12083},{"id":12082},"built-in-asyncawait-support",[12084,12086,12091,12092,12098],{"type":27,"value":12085},"Built-in ",{"type":22,"tag":486,"props":12087,"children":12089},{"className":12088},[],[12090],{"type":27,"value":5561},{"type":27,"value":7252},{"type":22,"tag":486,"props":12093,"children":12095},{"className":12094},[],[12096],{"type":27,"value":12097},"await",{"type":27,"value":12099}," support",{"type":22,"tag":23,"props":12101,"children":12102},{},[12103,12105,12121,12123,12129,12130,12135,12137,12148],{"type":27,"value":12104},"While it's a small feature, Fastify's route handlers support ",{"type":22,"tag":30,"props":12106,"children":12109},{"href":12107,"rel":12108},"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function",[34],[12110,12115,12116],{"type":22,"tag":486,"props":12111,"children":12113},{"className":12112},[],[12114],{"type":27,"value":5561},{"type":27,"value":7252},{"type":22,"tag":486,"props":12117,"children":12119},{"className":12118},[],[12120],{"type":27,"value":12097},{"type":27,"value":12122}," syntax by default, catching errors and turning them into HTTP 500 responses. In Express, async errors are unhandled unless handled manually with a ",{"type":22,"tag":486,"props":12124,"children":12126},{"className":12125},[],[12127],{"type":27,"value":12128},"try",{"type":27,"value":7252},{"type":22,"tag":486,"props":12131,"children":12133},{"className":12132},[],[12134],{"type":27,"value":6136},{"type":27,"value":12136}," or with an added dependency like ",{"type":22,"tag":30,"props":12138,"children":12141},{"href":12139,"rel":12140},"https://github.com/Abazhenov/express-async-handler",[34],[12142],{"type":22,"tag":486,"props":12143,"children":12145},{"className":12144},[],[12146],{"type":27,"value":12147},"express-async-handler",{"type":27,"value":71},{"type":22,"tag":1481,"props":12150,"children":12152},{"id":12151},"built-in-request-validation",[12153],{"type":27,"value":12154},"Built-in request validation",{"type":22,"tag":23,"props":12156,"children":12157},{},[12158,12160,12167,12169,12176,12178,12184],{"type":27,"value":12159},"Something that's often missing from Express projects is comprehensive request validation, and this is a source of bugs and security vulnerabilities. Fastify has a ",{"type":22,"tag":30,"props":12161,"children":12164},{"href":12162,"rel":12163},"https://www.fastify.io/docs/latest/Reference/Validation-and-Serialization/",[34],[12165],{"type":27,"value":12166},"robust validation system",{"type":27,"value":12168}," based on the ",{"type":22,"tag":30,"props":12170,"children":12173},{"href":12171,"rel":12172},"https://json-schema.org/",[34],[12174],{"type":27,"value":12175},"JSON Schema",{"type":27,"value":12177}," specification, making validation as easy as adding a ",{"type":22,"tag":486,"props":12179,"children":12181},{"className":12180},[],[12182],{"type":27,"value":12183},"schema",{"type":27,"value":12185}," key to a route's options.",{"type":22,"tag":1481,"props":12187,"children":12189},{"id":12188},"large-ecosystem-of-core-plugins",[12190],{"type":27,"value":12191},"Large ecosystem of \"core\" plugins",{"type":22,"tag":23,"props":12193,"children":12194},{},[12195,12197,12204],{"type":27,"value":12196},"Fastify's ",{"type":22,"tag":30,"props":12198,"children":12201},{"href":12199,"rel":12200},"https://www.fastify.io/docs/latest/Guides/Ecosystem/",[34],[12202],{"type":27,"value":12203},"plugin ecosystem",{"type":27,"value":12205}," is comparable to Express, but one thing that sets it apart is that many of these plugins are \"core\" plugins maintained by the Fastify team themselves. A project's Achilles' heel can be a dependency on some community third-party library whose author has dropped off the radar, and yet this risk is not always apparent when developers are choosing to take on a new dependency. Having a large list of core plugins to choose from means having a lot of additional functionality at your disposal without increasing your project's dependence on third parties.",{"type":22,"tag":1481,"props":12207,"children":12209},{"id":12208},"built-in-testing-capabilities",[12210],{"type":27,"value":12211},"Built-in testing capabilities",{"type":22,"tag":23,"props":12213,"children":12214},{},[12215],{"type":27,"value":12216},"Express does not provide any functionality intended for testing, which means projects tend to either",{"type":22,"tag":847,"props":12218,"children":12219},{},[12220,12232,12237],{"type":22,"tag":115,"props":12221,"children":12222},{},[12223,12225,12230],{"type":27,"value":12224},"start up a full Webserver and use a tool like ",{"type":22,"tag":30,"props":12226,"children":12228},{"href":4529,"rel":12227},[34],[12229],{"type":27,"value":4533},{"type":27,"value":12231}," to test via real API requests,",{"type":22,"tag":115,"props":12233,"children":12234},{},[12235],{"type":27,"value":12236},"expose route handlers and test them independently of the routing, or",{"type":22,"tag":115,"props":12238,"children":12239},{},[12240],{"type":27,"value":12241},"skip integration tests entirely.",{"type":22,"tag":23,"props":12243,"children":12244},{},[12245],{"type":27,"value":12246},"Integration testing against a running Webserver is slow, and it's brittle given how limited the options are for setting up test data or mocking external services. Testing route handlers is better, but it leaves a gap in test coverage (the untested routing/middleware/validation).",{"type":22,"tag":23,"props":12248,"children":12249},{},[12250,12252,12259,12261,12267],{"type":27,"value":12251},"Fastify, on the other hand, provides ",{"type":22,"tag":30,"props":12253,"children":12256},{"href":12254,"rel":12255},"https://www.fastify.io/docs/latest/Guides/Testing/",[34],[12257],{"type":27,"value":12258},"testing utilities",{"type":27,"value":12260}," which allow for mock requests to be processed using a ",{"type":22,"tag":486,"props":12262,"children":12264},{"className":12263},[],[12265],{"type":27,"value":12266},".inject()",{"type":27,"value":12268}," method, without spinning up a Webserver. This means tests are fast, and they allow for arbitrary setup/teardown logic (like opening database connections and loading test data), while still being test framework independent.",{"type":22,"tag":1481,"props":12270,"children":12272},{"id":12271},"typescript-compatibility",[12273],{"type":27,"value":12274},"TypeScript compatibility",{"type":22,"tag":23,"props":12276,"children":12277},{},[12278],{"type":27,"value":12279},"There are TypeScript typings for Express, and Fastify is similarly a JS project with added TypeScript typings (as opposed to being designed from the ground up to leverage a type system).",{"type":22,"tag":23,"props":12281,"children":12282},{},[12283,12285,12292,12294,12300,12301,12307,12308,12314],{"type":27,"value":12284},"However, Fastify also provides ",{"type":22,"tag":30,"props":12286,"children":12289},{"href":12287,"rel":12288},"https://www.fastify.io/docs/latest/Reference/TypeScript/",[34],[12290],{"type":27,"value":12291},"official documentation",{"type":27,"value":12293}," for usage in TypeScript, which in particular shows its really great capability (via an added library) of building TypeScript types out of request validation schemas. What this means is that when using TypeScript, the ",{"type":22,"tag":486,"props":12295,"children":12297},{"className":12296},[],[12298],{"type":27,"value":12299},"request.params",{"type":27,"value":236},{"type":22,"tag":486,"props":12302,"children":12304},{"className":12303},[],[12305],{"type":27,"value":12306},"request.query",{"type":27,"value":1441},{"type":22,"tag":486,"props":12309,"children":12311},{"className":12310},[],[12312],{"type":27,"value":12313},"request.body",{"type":27,"value":12315}," fields will have types matching their validated content, ensuring that the validation code and the application code are in sync.",{"type":22,"tag":1481,"props":12317,"children":12319},{"id":12318},"and-more",[12320],{"type":27,"value":12321},"...and more",{"type":22,"tag":111,"props":12323,"children":12324},{},[12325,12330,12342],{"type":22,"tag":115,"props":12326,"children":12327},{},[12328],{"type":27,"value":12329},"Built-in JSON support such that route handlers don't need to encode/decode JSON manually, which is a nice convenience",{"type":22,"tag":115,"props":12331,"children":12332},{},[12333,12335],{"type":27,"value":12334},"Built-in logging via ",{"type":22,"tag":30,"props":12336,"children":12339},{"href":12337,"rel":12338},"https://github.com/pinojs/pino",[34],[12340],{"type":27,"value":12341},"pino",{"type":22,"tag":115,"props":12343,"children":12344},{},[12345,12347],{"type":27,"value":12346},"Speed, with Fastify performancing much better than Express in ",{"type":22,"tag":30,"props":12348,"children":12351},{"href":12349,"rel":12350},"https://www.fastify.io/benchmarks/",[34],[12352],{"type":27,"value":12353},"benchmarks",{"type":22,"tag":23,"props":12355,"children":12356},{},[12357],{"type":27,"value":12358},"There are many other quality JavaScript Web frameworks to consider, but without going into a full comparison, I don't think they fill the role of Express quite so well as Fastify does:",{"type":22,"tag":111,"props":12360,"children":12361},{},[12362,12374,12386],{"type":22,"tag":115,"props":12363,"children":12364},{},[12365,12372],{"type":22,"tag":30,"props":12366,"children":12369},{"href":12367,"rel":12368},"https://nestjs.com/",[34],[12370],{"type":27,"value":12371},"NestJS",{"type":27,"value":12373}," is great in its own right as a heavyweight, opinionated framework (which can optionally be based on Fastify), but it's not a good fit for small projects or ones which need a lot of flexibility.",{"type":22,"tag":115,"props":12375,"children":12376},{},[12377,12384],{"type":22,"tag":30,"props":12378,"children":12381},{"href":12379,"rel":12380},"https://koajs.com/",[34],[12382],{"type":27,"value":12383},"Koa",{"type":27,"value":12385}," is great for being very similar to Express while still adding some welcome features, but it doesn't have nearly the feature set of Fastify.",{"type":22,"tag":115,"props":12387,"children":12388},{},[12389,12396],{"type":22,"tag":30,"props":12390,"children":12393},{"href":12391,"rel":12392},"https://adonisjs.com/",[34],[12394],{"type":27,"value":12395},"AdonisJS",{"type":27,"value":12397}," is pretty comparable to Fastify and it's TypeScript-first, but it doesn't have quite the same plugin ecosystem.",{"type":22,"tag":193,"props":12399,"children":12401},{"id":12400},"pros-and-cons-of-migrating-from-express-to-fastify",[12402],{"type":27,"value":12403},"Pros and Cons of Migrating from Express to Fastify",{"type":22,"tag":23,"props":12405,"children":12406},{},[12407],{"type":27,"value":12408},"For a greenfield Node.js project, I would readily choose Fastify over Express; it just gives developers access to so many added features, all in an opt-in fashion so they don't get in the way, in a well-maintained project that feels like it will still be around in five years' time.",{"type":22,"tag":23,"props":12410,"children":12411},{},[12412],{"type":27,"value":12413},"Whether or not to migrate an existing Express project to Fastify, however, is a more difficult question.",{"type":22,"tag":23,"props":12415,"children":12416},{},[12417,12419,12428],{"type":27,"value":12418},"Such a migration doesn't need to happen in a single step; there's a ",{"type":22,"tag":30,"props":12420,"children":12422},{"href":4385,"rel":12421},[34],[12423],{"type":22,"tag":486,"props":12424,"children":12426},{"className":12425},[],[12427],{"type":27,"value":4393},{"type":27,"value":12429}," plugin which allows an entire Express router to be loaded inside of a Fastify application, allowing both to exist simultaneously for a time. The plugin documentation advises against using it as a long-term solution, so it is intended to be part of a full migration and not for maintaining a set of legacy Express routes long-term. Still, Fastify is different enough from Express that a migration is time-consuming, and differences in its request and response objects means that application code in the route handlers needs to be touched as well.",{"type":22,"tag":23,"props":12431,"children":12432},{},[12433,12435,12440],{"type":27,"value":12434},"While Fastify provides a ton of added functionality, almost all that functionality can be tacked on to Express via third-party libraries, so there's little reason why a project would flat-out ",{"type":22,"tag":267,"props":12436,"children":12437},{},[12438],{"type":27,"value":12439},"need",{"type":27,"value":12441}," to migrate. Express gets the job done; unless the project has a long life ahead, good test coverage to cover a migration, and tens of hours to spare to implement it, it's probably best to stick with Express.",{"type":22,"tag":23,"props":12443,"children":12444},{},[12445,12447,12453,12455,12460],{"type":27,"value":12446},"...That said, in order to come to that conlusion I needed to see just how easy (or hard) it was to migrate a project, so that's just what I did. In Part 2 we'll look at the process of converting an ",{"type":22,"tag":30,"props":12448,"children":12450},{"href":4338,"rel":12449},[34],[12451],{"type":27,"value":12452},"Express implementation",{"type":27,"value":12454}," of the ",{"type":22,"tag":30,"props":12456,"children":12458},{"href":4351,"rel":12457},[34],[12459],{"type":27,"value":4355},{"type":27,"value":12461},", to get an idea of what it looks like to migrate a non-trivial application to Fastify.",{"title":8,"searchDepth":755,"depth":755,"links":12463},[12464,12475],{"id":12027,"depth":758,"text":12030,"children":12465},[12466,12467,12468,12470,12471,12472,12473,12474],{"id":12043,"depth":755,"text":12046},{"id":12062,"depth":755,"text":12065},{"id":12082,"depth":755,"text":12469},"Built-in async/await support",{"id":12151,"depth":755,"text":12154},{"id":12188,"depth":755,"text":12191},{"id":12208,"depth":755,"text":12211},{"id":12271,"depth":755,"text":12274},{"id":12318,"depth":755,"text":12321},{"id":12400,"depth":758,"text":12403},"content:phendry:2022-07-21:MigratingFromExpressToFastifyPart1.md","phendry/2022-07-21/MigratingFromExpressToFastifyPart1.md","phendry/2022-07-21/MigratingFromExpressToFastifyPart1",{"user":774,"name":775},{"_path":12481,"_dir":12482,"_draft":7,"_partial":7,"_locale":8,"title":12483,"description":12484,"image":12485,"excerpt":12484,"publishDate":12486,"tags":12487,"body":12488,"_type":767,"_id":13158,"_source":769,"_file":13159,"_stem":13160,"_extension":772,"author":13161},"/phendry/2023-07-28/dependencymanagement","2023-07-28","Software Dependency Management: Best Practices","Leveraging third-party libraries and frameworks is essential in most modern software projects, and the projects we build at Art+Logic are no exception. The pressure on developers to rapidly deliver features is high, and there are so many commonalities in the details of each project (particularly in Web development) that a lot of development time can be saved by using well-designed libraries that handle the details.","/phendry/2023-07-28/img/dependency_header.png","2023-01-02",[12],{"type":19,"children":12489,"toc":13149},[12490,12494,12499,12527,12532,12560,12565,12570,12576,12581,12680,12686,12691,12705,12710,12895,12900,12906,12911,12916,12934,12946,12952,12963,12968,12974,13094,13100],{"type":22,"tag":23,"props":12491,"children":12492},{},[12493],{"type":27,"value":12484},{"type":22,"tag":23,"props":12495,"children":12496},{},[12497],{"type":27,"value":12498},"Taking on an external project as a dependency also carries a number of risks however, risks that range from minor to downright catastrophic. Third-party projects can",{"type":22,"tag":111,"props":12500,"children":12501},{},[12502,12507,12512,12517,12522],{"type":22,"tag":115,"props":12503,"children":12504},{},[12505],{"type":27,"value":12506},"lose maintainers without warning, and consequently become insecure or nonfunctional through lack of updates,",{"type":22,"tag":115,"props":12508,"children":12509},{},[12510],{"type":27,"value":12511},"become compromised by malicious code which in turn compromises deployed environments or developer machines,",{"type":22,"tag":115,"props":12513,"children":12514},{},[12515],{"type":27,"value":12516},"lead to software bloat that negatively impacts application performance,",{"type":22,"tag":115,"props":12518,"children":12519},{},[12520],{"type":27,"value":12521},"cause maintenance problems by adding more API surface for developers to learn, and",{"type":22,"tag":115,"props":12523,"children":12524},{},[12525],{"type":27,"value":12526},"create painful upgrade/migration scenarios which consume developer resources.",{"type":22,"tag":23,"props":12528,"children":12529},{},[12530],{"type":27,"value":12531},"In other words, while there's a lot to be gained, there's also a lot that can occasionally go wrong. A fate suffered by many a software project goes something like this:",{"type":22,"tag":847,"props":12533,"children":12534},{},[12535,12540,12545,12550,12555],{"type":22,"tag":115,"props":12536,"children":12537},{},[12538],{"type":27,"value":12539},"At the outset of the project, a few core dependencies are selected. They're vetted to be well-maintained projects that will remain so for a long time.",{"type":22,"tag":115,"props":12541,"children":12542},{},[12543],{"type":27,"value":12544},"As time goes by, further dependencies are added one by one, each solving a particular problem. Whether it's justified to add them or not, each adds a little more to build times, to application size, and sometimes to application latency. The dependencies are rarely updated, because doing so is behind-the-scenes maintenance work whose value is difficult to appreciate; it incurs development and testing costs without improving the end user experience.",{"type":22,"tag":115,"props":12546,"children":12547},{},[12548],{"type":27,"value":12549},"One day, a high-priority dependency-related issue arises. Perhaps the old version of a library stops working and breaks the application until it is updated, or perhaps a critical feature cannot be implemented without the features provided by a newer version of a dependency. The team works to upgrade that dependency.",{"type":22,"tag":115,"props":12551,"children":12552},{},[12553],{"type":27,"value":12554},"In order to upgrade that one dependency, other related dependencies need to be upgraded as well. Soon, many dependencies have been upgraded, and many code changes have been made in order to migrate to their new APIs. The process takes many hours, and introduces many regressions because it was done as one sweeping change rather than as individual upgrades that could have been tested more granularly.",{"type":22,"tag":115,"props":12556,"children":12557},{},[12558],{"type":27,"value":12559},"What looked like a simple bug or feature request turns into tens of hours of maintenance work, and stakeholders are unhappy.",{"type":22,"tag":23,"props":12561,"children":12562},{},[12563],{"type":27,"value":12564},"There is a sweet spot somewhere in between \"reinventing the wheel\" and\n\"dependency hell\", and while it differs from project to project, it sits closer\nto the \"reinventing the wheel\" side than one might intuitively expect. That's\nbecause the positive effects of adding a dependency are usually very immediate\n(\"I had a problem, now this package solves my problem\"), whereas the negative\neffects are delayed (since they mostly relate to long-term maintenance).\nConsequently, over-use of third-party dependencies is a form of tech debt,\nquickly solving problems in the short term but causing larger ones in the long\nterm.",{"type":22,"tag":23,"props":12566,"children":12567},{},[12568],{"type":27,"value":12569},"In this post, we'll outline some rules to follow in order to minimize the risks posed by third-party dependencies, with the aim of ensuring the long-term stability of a project.",{"type":22,"tag":193,"props":12571,"children":12573},{"id":12572},"rule-1-minimize-the-number-of-dependencies",[12574],{"type":27,"value":12575},"Rule #1: Minimize the Number of Dependencies",{"type":22,"tag":23,"props":12577,"children":12578},{},[12579],{"type":27,"value":12580},"It may be obvious, but the fewer dependencies a project has, the lower the risk they pose. Here are some strategies to achieve this:",{"type":22,"tag":111,"props":12582,"children":12583},{},[12584,12602,12635,12653,12670],{"type":22,"tag":115,"props":12585,"children":12586},{},[12587,12592,12594],{"type":22,"tag":2598,"props":12588,"children":12589},{},[12590],{"type":27,"value":12591},"Consolidate libraries:",{"type":27,"value":12593}," If one library can do a reasonable job at filling the role of two or more others, then prefer the single multipurpose library over the multiple specialized ones.\n",{"type":22,"tag":111,"props":12595,"children":12596},{},[12597],{"type":22,"tag":115,"props":12598,"children":12599},{},[12600],{"type":27,"value":12601},"Sometimes the multipurpose option has limitations, or it is not as nice to use. However, it also means fewer library maintainers to rely on, fewer sources of documentation for developers to locate, and fewer package updates to manage.",{"type":22,"tag":115,"props":12603,"children":12604},{},[12605,12610,12612],{"type":22,"tag":2598,"props":12606,"children":12607},{},[12608],{"type":27,"value":12609},"Borrow Code:",{"type":27,"value":12611}," If the code being leveraged is small and self-contained, if library's license allows for copying the code, and if that code has little reason to change in the future, then copy it into the project rather than linking to it as a dependency. We gain the functionality without needing to maintain another dependency.\n",{"type":22,"tag":111,"props":12613,"children":12614},{},[12615,12625,12630],{"type":22,"tag":115,"props":12616,"children":12617},{},[12618,12620],{"type":27,"value":12619},"This is obviously not appropriate when it's illegal. ",{"type":22,"tag":267,"props":12621,"children":12622},{},[12623],{"type":27,"value":12624},"Check the library's license first.",{"type":22,"tag":115,"props":12626,"children":12627},{},[12628],{"type":27,"value":12629},"This not appropriate when the code has reason to change (in particular when its use has security implications). In that case, it's valuable to be able to continue accessing future updates from the upstream maintainer.",{"type":22,"tag":115,"props":12631,"children":12632},{},[12633],{"type":27,"value":12634},"Ensure that any borrowed code is tagged with code comments indicating its origin, in case it needs to be understood in the future.",{"type":22,"tag":115,"props":12636,"children":12637},{},[12638,12643,12645],{"type":22,"tag":2598,"props":12639,"children":12640},{},[12641],{"type":27,"value":12642},"Do it yourself:",{"type":27,"value":12644}," If the functionality is easy to implement yourself, then do it, rather than rely on a dependency.\n",{"type":22,"tag":111,"props":12646,"children":12647},{},[12648],{"type":22,"tag":115,"props":12649,"children":12650},{},[12651],{"type":27,"value":12652},"Look out for icebergs; many things look easy from the surface but are full of complexity underneath and are best left to domain experts.",{"type":22,"tag":115,"props":12654,"children":12655},{},[12656,12661,12663,12668],{"type":22,"tag":2598,"props":12657,"children":12658},{},[12659],{"type":27,"value":12660},"Keep it simple:",{"type":27,"value":12662}," Take on dependencies when you ",{"type":22,"tag":267,"props":12664,"children":12665},{},[12666],{"type":27,"value":12667},"must",{"type":27,"value":12669}," in order to deliver a maintainable product, not when it makes only marginal improvements.",{"type":22,"tag":115,"props":12671,"children":12672},{},[12673,12678],{"type":22,"tag":2598,"props":12674,"children":12675},{},[12676],{"type":27,"value":12677},"Separate your runtime and development dependencies:",{"type":27,"value":12679}," Many package managers have features or workflows for listing runtime dependencies separately from development dependencies. Since development dependencies aren't deployed, they pose fewer risks, so we you be somewhat more liberal with them than with runtime dependencies.",{"type":22,"tag":193,"props":12681,"children":12683},{"id":12682},"rule-2-audit-dependencies",[12684],{"type":27,"value":12685},"Rule #2: Audit Dependencies",{"type":22,"tag":23,"props":12687,"children":12688},{},[12689],{"type":27,"value":12690},"All projects ought to be audited before being added as a dependency: this means reviewing the project to ensure that it is secure, well-implemented, and well-maintained.",{"type":22,"tag":23,"props":12692,"children":12693},{},[12694,12696,12703],{"type":27,"value":12695},"For NPM and PyPi packages, ",{"type":22,"tag":30,"props":12697,"children":12700},{"href":12698,"rel":12699},"https://snyk.io/advisor/",[34],[12701],{"type":27,"value":12702},"Snyk Advisor",{"type":27,"value":12704}," is a useful\nstarting point for assessing a package's health. It algorithmically scores a\npackage based on things like reported security vulnerabilities, frequency of\nmaintenance, number of contributors, and number of downloads.",{"type":22,"tag":23,"props":12706,"children":12707},{},[12708],{"type":27,"value":12709},"Here is a checklist for auditing a candidate (or existing) dependency:",{"type":22,"tag":847,"props":12711,"children":12712},{},[12713,12731,12752,12843],{"type":22,"tag":115,"props":12714,"children":12715},{},[12716,12718],{"type":27,"value":12717},"Security\n",{"type":22,"tag":111,"props":12719,"children":12720},{},[12721,12726],{"type":22,"tag":115,"props":12722,"children":12723},{},[12724],{"type":27,"value":12725},"☐ Has no history of security vulnerabilities, OR past security vulnerabilities have been taken seriously and patched promptly.",{"type":22,"tag":115,"props":12727,"children":12728},{},[12729],{"type":27,"value":12730},"☐ Has a documented policy for reporting and responding to security issues.",{"type":22,"tag":115,"props":12732,"children":12733},{},[12734,12736],{"type":27,"value":12735},"Popularity\n",{"type":22,"tag":111,"props":12737,"children":12738},{},[12739],{"type":22,"tag":115,"props":12740,"children":12741},{},[12742,12744],{"type":27,"value":12743},"☐ Has a healthy popularity (e.g. downloads per month).\n",{"type":22,"tag":111,"props":12745,"children":12746},{},[12747],{"type":22,"tag":115,"props":12748,"children":12749},{},[12750],{"type":27,"value":12751},"This is a proxy for how much demand there is to continue maintaining the project, and how likely it is that it would be forked by the community if the current maintainers were to disappear.",{"type":22,"tag":115,"props":12753,"children":12754},{},[12755,12757],{"type":27,"value":12756},"Maintenance\n",{"type":22,"tag":111,"props":12758,"children":12759},{},[12760,12780,12799,12804,12817,12830],{"type":22,"tag":115,"props":12761,"children":12762},{},[12763,12765],{"type":27,"value":12764},"☐ Has a steady pace of development up to present-day.\n",{"type":22,"tag":111,"props":12766,"children":12767},{},[12768],{"type":22,"tag":115,"props":12769,"children":12770},{},[12771,12773,12778],{"type":27,"value":12772},"Check for projects which may ",{"type":22,"tag":267,"props":12774,"children":12775},{},[12776],{"type":27,"value":12777},"look",{"type":27,"value":12779}," active due to historic popularity, but which are no longer being updated.",{"type":22,"tag":115,"props":12781,"children":12782},{},[12783,12785],{"type":27,"value":12784},"☐ Has a steady pace of releases.\n",{"type":22,"tag":111,"props":12786,"children":12787},{},[12788],{"type":22,"tag":115,"props":12789,"children":12790},{},[12791,12793,12797],{"type":27,"value":12792},"Note that this does ",{"type":22,"tag":267,"props":12794,"children":12795},{},[12796],{"type":27,"value":869},{"type":27,"value":12798}," follow automatically from a steady development pace (e.g. devs plugging away for years at a shiny new rewrite).",{"type":22,"tag":115,"props":12800,"children":12801},{},[12802],{"type":27,"value":12803},"☐ Has a history of prompt handling of bug reports and feature requests.",{"type":22,"tag":115,"props":12805,"children":12806},{},[12807,12809],{"type":27,"value":12808},"☐ Respects semantic versioning policy.\n",{"type":22,"tag":111,"props":12810,"children":12811},{},[12812],{"type":22,"tag":115,"props":12813,"children":12814},{},[12815],{"type":27,"value":12816},"It's a major issue if minor/patch releases can't be trusted not to have breaking changes.",{"type":22,"tag":115,"props":12818,"children":12819},{},[12820,12822],{"type":27,"value":12821},"☐ Provides thorough migration guides for major version upgrades.\n",{"type":22,"tag":111,"props":12823,"children":12824},{},[12825],{"type":22,"tag":115,"props":12826,"children":12827},{},[12828],{"type":27,"value":12829},"It's a major issue if you can't update a major version number with confidence.",{"type":22,"tag":115,"props":12831,"children":12832},{},[12833,12835],{"type":27,"value":12834},"☐ The source code looks like you could maintain or extend it if you needed to.\n",{"type":22,"tag":111,"props":12836,"children":12837},{},[12838],{"type":22,"tag":115,"props":12839,"children":12840},{},[12841],{"type":27,"value":12842},"You might need to, if the library gets abandoned someday.",{"type":22,"tag":115,"props":12844,"children":12845},{},[12846,12848],{"type":27,"value":12847},"Community\n",{"type":22,"tag":111,"props":12849,"children":12850},{},[12851,12869,12882],{"type":22,"tag":115,"props":12852,"children":12853},{},[12854,12856],{"type":27,"value":12855},"☐ Has a healthy number of contributors.\n",{"type":22,"tag":111,"props":12857,"children":12858},{},[12859,12864],{"type":22,"tag":115,"props":12860,"children":12861},{},[12862],{"type":27,"value":12863},"Use a tool like GitHub's \"Insights\" tab to view contributor statistics.",{"type":22,"tag":115,"props":12865,"children":12866},{},[12867],{"type":27,"value":12868},"The definition of \"healthy\" will depend on the project, but generally you want to see that the project owner accepts contributions from others, and that those contributions make up a meaningful portion of the codebase.",{"type":22,"tag":115,"props":12870,"children":12871},{},[12872,12874],{"type":27,"value":12873},"☐ Has good developer documentation to assist new contributors.\n",{"type":22,"tag":111,"props":12875,"children":12876},{},[12877],{"type":22,"tag":115,"props":12878,"children":12879},{},[12880],{"type":27,"value":12881},"This shows interest in having many contributors rather than a one-man show, and also helps you if you ever had to pick up an abandoned project.",{"type":22,"tag":115,"props":12883,"children":12884},{},[12885,12887],{"type":27,"value":12886},"☐ Has funding (e.g. corporate sponsorships or donations).\n",{"type":22,"tag":111,"props":12888,"children":12889},{},[12890],{"type":22,"tag":115,"props":12891,"children":12892},{},[12893],{"type":27,"value":12894},"A project with funding is more likely to stick around than one that's maintained by hobbyists.",{"type":22,"tag":23,"props":12896,"children":12897},{},[12898],{"type":27,"value":12899},"If more than one of those boxes is unchecked, then the package is at higher risk\nof posing future security or maintenance problems, and consequently it should be avoided if a\nbetter alternative exists, or else the risks should be communicated to project management\nand/or stakeholders as appropriate.",{"type":22,"tag":193,"props":12901,"children":12903},{"id":12902},"rule-3-keep-dependencies-up-to-date",[12904],{"type":27,"value":12905},"Rule #3: Keep Dependencies Up-to-date",{"type":22,"tag":23,"props":12907,"children":12908},{},[12909],{"type":27,"value":12910},"Typically, a project's dependencies should be updated on a regular cadence. This is not just to receive security patches and other bugfixes, nor is it so that developers can have the newest and shiniest features; it's to prevent the situation described earlier in this post where out-of-date dependencies need to be suddenly and immediately made current, a long and error-prone process that can grind a project to a halt.",{"type":22,"tag":23,"props":12912,"children":12913},{},[12914],{"type":27,"value":12915},"Like other forms of technical debt reduction however, it can be difficult to convince stakeholders of the importance of budgeting some time away from immediate end product improvements and towards keeping dependencies up-to-date. At Art+Logic, we approach this topic with our clients by:",{"type":22,"tag":111,"props":12917,"children":12918},{},[12919,12924,12929],{"type":22,"tag":115,"props":12920,"children":12921},{},[12922],{"type":27,"value":12923},"Building the client's trust in us, by establishing a track record of rapidly delivering them high-quality results. When a client trusts that we're not out to talk them into things they don't need, they're more likely to be interested in hearing the team's maintenance priorities.",{"type":22,"tag":115,"props":12925,"children":12926},{},[12927],{"type":27,"value":12928},"Communicating the trade-offs of the chosen development pattern. It's not necessarily a bad decision for a project to prioritize feature development over reducing technical debt or risk; what's important however is that the client understands the trade-off they're making.",{"type":22,"tag":115,"props":12930,"children":12931},{},[12932],{"type":27,"value":12933},"When possible, agreeing upon a regular maintenance budget that the team can use for, among other things, keeping dependencies up-to-date. This relieves both the team and the client from having to routinely negotiate maintenance efforts, and it serves as an explicit agreement between the team and the client of what the balance should be between short-term features and long-term stability.",{"type":22,"tag":23,"props":12935,"children":12936},{},[12937,12939,12944],{"type":27,"value":12938},"As far as the procedure goes for applying updates, the focus should be on doing it as atomically as possible; that is, to do it in small steps and return to a functioning application in between each step. If everything is updated all at once, then in the likely scenario that this breaks the application until some fixes are made, it is ",{"type":22,"tag":267,"props":12940,"children":12941},{},[12942],{"type":27,"value":12943},"much",{"type":27,"value":12945}," harder to identify the root causes of each bug. A little time spent recompiling and smoke-testing the application in between each upgrade saves a lot of time spent struggling to get a broken application working again.",{"type":22,"tag":193,"props":12947,"children":12949},{"id":12948},"final-thoughts-and-language-specific-advice",[12950],{"type":27,"value":12951},"Final Thoughts, and Language-specific Advice",{"type":22,"tag":23,"props":12953,"children":12954},{},[12955,12957,12961],{"type":27,"value":12956},"Often, the biggest challenge with managing the dependencies of a software project is just finding the time or the budget to do it. Since the consequences of letting dependencies gather dust for too long can be so severe however, it's worth finding a away. It's also important for teams to always be asking themselves the question: \"do we really ",{"type":22,"tag":267,"props":12958,"children":12959},{},[12960],{"type":27,"value":12439},{"type":27,"value":12962}," this library?\" Keeping the dependency list small is the easiest way to avoid complications further down the road.",{"type":22,"tag":23,"props":12964,"children":12965},{},[12966],{"type":27,"value":12967},"Each language and ecosystem has its own tools which can help with these efforts. We'll close out here with a few recommendations for programming languages we frequently work with:",{"type":22,"tag":1481,"props":12969,"children":12971},{"id":12970},"javascript",[12972],{"type":27,"value":12973},"JavaScript",{"type":22,"tag":111,"props":12975,"children":12976},{},[12977,13010],{"type":22,"tag":115,"props":12978,"children":12979},{},[12980,12982,12993,12995,13001,13003,13008],{"type":27,"value":12981},"Use ",{"type":22,"tag":30,"props":12983,"children":12986},{"href":12984,"rel":12985},"https://www.npmjs.com/package/npm-check-updates",[34],[12987],{"type":22,"tag":486,"props":12988,"children":12990},{"className":12989},[],[12991],{"type":27,"value":12992},"npm-check-updates",{"type":27,"value":12994}," to conveniently check for package updates. It has a ",{"type":22,"tag":486,"props":12996,"children":12998},{"className":12997},[],[12999],{"type":27,"value":13000},"--upgrade",{"type":27,"value":13002}," option which writes all version updates to ",{"type":22,"tag":486,"props":13004,"children":13006},{"className":13005},[],[13007],{"type":27,"value":10192},{"type":27,"value":13009}," automatically; it's often worth giving that a shot to see if you're lucky enough to have no issues, and then if there are breakages simply revert it and apply the updates one by one.",{"type":22,"tag":115,"props":13011,"children":13012},{},[13013,13015,13026,13028,13034,13036,13041,13043,13049,13051,13056,13058,13063,13065,13071,13073,13078,13080,13085,13087,13092],{"type":27,"value":13014},"Get in the habit of using ",{"type":22,"tag":30,"props":13016,"children":13019},{"href":13017,"rel":13018},"https://docs.npmjs.com/cli/v9/commands/npm-ci",[34],[13020],{"type":22,"tag":486,"props":13021,"children":13023},{"className":13022},[],[13024],{"type":27,"value":13025},"npm ci",{"type":27,"value":13027}," as your default alternative to ",{"type":22,"tag":486,"props":13029,"children":13031},{"className":13030},[],[13032],{"type":27,"value":13033},"npm install",{"type":27,"value":13035},". The ",{"type":22,"tag":486,"props":13037,"children":13039},{"className":13038},[],[13040],{"type":27,"value":13033},{"type":27,"value":13042}," command is commonly used in order to make locally installed packages match what's defined in the repository's ",{"type":22,"tag":486,"props":13044,"children":13046},{"className":13045},[],[13047],{"type":27,"value":13048},"package*.json",{"type":27,"value":13050}," files, but ",{"type":22,"tag":486,"props":13052,"children":13054},{"className":13053},[],[13055],{"type":27,"value":13033},{"type":27,"value":13057}," ",{"type":22,"tag":267,"props":13059,"children":13060},{},[13061],{"type":27,"value":13062},"updates",{"type":27,"value":13064}," the ",{"type":22,"tag":486,"props":13066,"children":13068},{"className":13067},[],[13069],{"type":27,"value":13070},"package-lock.json",{"type":27,"value":13072}," file if it finds newer versions. This is typically not something you want to happen implicitly! ",{"type":22,"tag":486,"props":13074,"children":13076},{"className":13075},[],[13077],{"type":27,"value":13025},{"type":27,"value":13079}," will only install ",{"type":22,"tag":267,"props":13081,"children":13082},{},[13083],{"type":27,"value":13084},"exactly",{"type":27,"value":13086}," what's in ",{"type":22,"tag":486,"props":13088,"children":13090},{"className":13089},[],[13091],{"type":27,"value":13070},{"type":27,"value":13093},", leaving upgrades to happen as a separate and explicit action.",{"type":22,"tag":1481,"props":13095,"children":13097},{"id":13096},"python",[13098],{"type":27,"value":13099},"Python",{"type":22,"tag":111,"props":13101,"children":13102},{},[13103],{"type":22,"tag":115,"props":13104,"children":13105},{},[13106,13108,13115,13117,13123,13125,13131,13133,13139,13141,13147],{"type":27,"value":13107},"Consider using ",{"type":22,"tag":30,"props":13109,"children":13112},{"href":13110,"rel":13111},"https://python-poetry.org/",[34],[13113],{"type":27,"value":13114},"Poetry",{"type":27,"value":13116}," as a package manager, rather than a ",{"type":22,"tag":486,"props":13118,"children":13120},{"className":13119},[],[13121],{"type":27,"value":13122},"pip freeze > requirements.txt",{"type":27,"value":13124}," approach. ",{"type":22,"tag":486,"props":13126,"children":13128},{"className":13127},[],[13129],{"type":27,"value":13130},"pip freeze",{"type":27,"value":13132}," lumps transitive dependencies in with direct dependencies, making it impractical to go back and determine which packages are still in use. Poetry on the other hand uses a \"lockfile\" approach, where there are two files: the manifest defining the desired packages (",{"type":22,"tag":486,"props":13134,"children":13136},{"className":13135},[],[13137],{"type":27,"value":13138},"pyproject.toml",{"type":27,"value":13140},"), and a separate lockfile that specifies the exact version to install for each package (",{"type":22,"tag":486,"props":13142,"children":13144},{"className":13143},[],[13145],{"type":27,"value":13146},"poetry.lock",{"type":27,"value":13148},"). The lockfile provides the reproducibility needed for deployments, while the manifest file preserves your intentions, making it easy to review dependencies or to automatically upgrade them.",{"title":8,"searchDepth":755,"depth":755,"links":13150},[13151,13152,13153,13154],{"id":12572,"depth":758,"text":12575},{"id":12682,"depth":758,"text":12685},{"id":12902,"depth":758,"text":12905},{"id":12948,"depth":758,"text":12951,"children":13155},[13156,13157],{"id":12970,"depth":755,"text":12973},{"id":13096,"depth":755,"text":13099},"content:phendry:2023-07-28:DependencyManagement.md","phendry/2023-07-28/DependencyManagement.md","phendry/2023-07-28/DependencyManagement",{"user":774,"name":775},{"_path":13163,"_dir":13164,"_draft":7,"_partial":7,"_locale":8,"title":13165,"description":13166,"image":13167,"tags":13168,"excerpt":13166,"publishDate":13169,"body":13170,"_type":767,"_id":13467,"_source":769,"_file":13468,"_stem":13469,"_extension":772,"author":13470},"/phendry/2023-05-16/doyouneedacsspreprocessor","2023-05-16","Do You Need a CSS Preprocessor in 2023?","CSS preprocessors like Less, Sass and Stylus have long provided powerful features that vanilla CSS lacked: variables, nesting of rulesets, mixins, control flow constructs, etc. These days however, the feature gap is considerably narrower, and it's not so clear that the benefits of a preprocessor outweight the burdens of setting it up.","/phendry/2023-05-16/img/css_preprocessor_header.png",[12],"2023-01-01",{"type":19,"children":13171,"toc":13465},[13172,13202,13220,13243,13271,13276,13450,13455,13460],{"type":22,"tag":23,"props":13173,"children":13174},{},[13175,13177,13184,13185,13192,13193,13200],{"type":27,"value":13176},"CSS preprocessors like ",{"type":22,"tag":30,"props":13178,"children":13181},{"href":13179,"rel":13180},"https://lesscss.org/",[34],[13182],{"type":27,"value":13183},"Less",{"type":27,"value":236},{"type":22,"tag":30,"props":13186,"children":13189},{"href":13187,"rel":13188},"https://sass-lang.com/",[34],[13190],{"type":27,"value":13191},"Sass",{"type":27,"value":1441},{"type":22,"tag":30,"props":13194,"children":13197},{"href":13195,"rel":13196},"https://stylus-lang.com/",[34],[13198],{"type":27,"value":13199},"Stylus",{"type":27,"value":13201}," have long provided powerful features that vanilla CSS lacked: variables, nesting of rulesets, mixins, control flow constructs, etc. These days however, the feature gap is considerably narrower, and it's not so clear that the benefits of a preprocessor outweight the burdens of setting it up.",{"type":22,"tag":23,"props":13203,"children":13204},{},[13205,13207,13218],{"type":27,"value":13206},"It's not terribly hard to set up a CSS preprocessor, but still, it adds complexity to a frontend build system that oftentimes is frightfully complicated to begin with. The project's build system needs to transform files to CSS, which typically involves adding another project dependency (e.g. ",{"type":22,"tag":30,"props":13208,"children":13211},{"href":13209,"rel":13210},"https://webpack.js.org/loaders/sass-loader/",[34],[13212],{"type":22,"tag":486,"props":13213,"children":13215},{"className":13214},[],[13216],{"type":27,"value":13217},"sass-loader",{"type":27,"value":13219}," for Sass in Webpack). Quirks with the ways the preprocessor's import system differs from the project's JavaScript module system can cause issues. Getting sourcemaps to work so that styles can be viewed/debugged in the browser can similarly require fiddling. There's also the added cognitive load it places on developers, who will need to learn the preprocessor's ins and outs in addition to those of CSS itself. Projects should be kept as lean as possible, so it's unwise to add additional languages and build tooling unless the benefit they provide is substantial.",{"type":22,"tag":23,"props":13221,"children":13222},{},[13223,13225,13232,13234,13241],{"type":27,"value":13224},"As for the benefits, well, it's true that you don't have to look for long before you find one that vanilla CSS still lacks; ",{"type":22,"tag":30,"props":13226,"children":13229},{"href":13227,"rel":13228},"https://caniuse.com/css-nesting",[34],[13230],{"type":27,"value":13231},"nesting",{"type":27,"value":13233}," for example only just recently (as of May 2023) saw support land in Chrome and Safari, with no support yet in Firefox. But there's one extra tool which makes the difference here, and it's ",{"type":22,"tag":30,"props":13235,"children":13238},{"href":13236,"rel":13237},"https://postcss.org/",[34],[13239],{"type":27,"value":13240},"PostCSS",{"type":27,"value":13242},": with PostCSS, not-yet-supported CSS proposals can be transpiled into widely-supported CSS, which enables enough nice CSS features that you arguably don't need a preprocessor like Sass or Less.",{"type":22,"tag":23,"props":13244,"children":13245},{},[13246,13248,13253,13255,13260,13262,13269],{"type":27,"value":13247},"That may seem like a disingenuous take, given that PostCSS ",{"type":22,"tag":267,"props":13249,"children":13250},{},[13251],{"type":27,"value":13252},"itself",{"type":27,"value":13254}," is a plugin-based CSS preprocessor. But the difference is, ",{"type":22,"tag":267,"props":13256,"children":13257},{},[13258],{"type":27,"value":13259},"you're already using PostCSS anyway",{"type":27,"value":13261},". Or at least, you should be, because of plugins like ",{"type":22,"tag":30,"props":13263,"children":13266},{"href":13264,"rel":13265},"https://github.com/postcss/autoprefixer",[34],[13267],{"type":27,"value":13268},"autoprefixer",{"type":27,"value":13270}," which automatically improve the browser compatibility of your styles. If you've already integrated PostCSS into your project, then it costs nothing (in terms of time and project complexity) to leverage it further.",{"type":22,"tag":23,"props":13272,"children":13273},{},[13274],{"type":27,"value":13275},"Here's a quick overview of how vanilla CSS and PostCSS plugins compare to the major selling points of other CSS preprocessors:",{"type":22,"tag":847,"props":13277,"children":13278},{},[13279,13297,13316,13326,13336,13400],{"type":22,"tag":115,"props":13280,"children":13281},{},[13282,13287,13289,13296],{"type":22,"tag":2598,"props":13283,"children":13284},{},[13285],{"type":27,"value":13286},"Variables:",{"type":27,"value":13288}," CSS Variables have ",{"type":22,"tag":30,"props":13290,"children":13293},{"href":13291,"rel":13292},"https://caniuse.com/css-variables",[34],[13294],{"type":27,"value":13295},"wide browser support",{"type":27,"value":71},{"type":22,"tag":115,"props":13298,"children":13299},{},[13300,13305,13307,13314],{"type":22,"tag":2598,"props":13301,"children":13302},{},[13303],{"type":27,"value":13304},"Nesting:",{"type":27,"value":13306}," Nesting is a real win for code readability when it's not overused. This is a ",{"type":22,"tag":30,"props":13308,"children":13311},{"href":13309,"rel":13310},"https://cssdb.org/#nesting-rules",[34],[13312],{"type":27,"value":13313},"Stage 2 CSS proposal",{"type":27,"value":13315},", meaning it requires a PostCSS plugin and is subject to change, but it's comparable in terms of capabilities to nesting in other preprocessors.",{"type":22,"tag":115,"props":13317,"children":13318},{},[13319,13324],{"type":22,"tag":2598,"props":13320,"children":13321},{},[13322],{"type":27,"value":13323},"Mixins and partials:",{"type":27,"value":13325}," CSS does not provide these concepts, however with variables and classes providing a lot of overlap capability-wise in terms of factoring out common rules, it's debatable whether the added capabilities of mixins/partials are a benefit or an overcomplication.",{"type":22,"tag":115,"props":13327,"children":13328},{},[13329,13334],{"type":22,"tag":2598,"props":13330,"children":13331},{},[13332],{"type":27,"value":13333},"Functions and Control Flow:",{"type":27,"value":13335}," Some preprocessors like Sass provide conditional and looping constructs, or even full-on scripting capabilities. CSS doesn't provide this, but it's another case where arguably this is achieved more cleanly in JavaScript rather than introducing scripting into stylesheets.",{"type":22,"tag":115,"props":13337,"children":13338},{},[13339,13344,13346],{"type":22,"tag":2598,"props":13340,"children":13341},{},[13342],{"type":27,"value":13343},"Color Functions:",{"type":27,"value":13345}," Most fancy ways to manipulate colors are not yet standardized in CSS, but existing proposals cover a lot of what preprocessors offer:\n",{"type":22,"tag":111,"props":13347,"children":13348},{},[13349,13369,13382],{"type":22,"tag":115,"props":13350,"children":13351},{},[13352,13358,13360,13367],{"type":22,"tag":486,"props":13353,"children":13355},{"className":13354},[],[13356],{"type":27,"value":13357},"color-mix()",{"type":27,"value":13359}," is a ",{"type":22,"tag":30,"props":13361,"children":13364},{"href":13362,"rel":13363},"https://cssdb.org/#color-contrast",[34],[13365],{"type":27,"value":13366},"Stage 2",{"type":27,"value":13368}," proposal that enables dynamically mixing colors together.",{"type":22,"tag":115,"props":13370,"children":13371},{},[13372,13374,13380],{"type":27,"value":13373},"Relative color syntax is a ",{"type":22,"tag":30,"props":13375,"children":13378},{"href":13376,"rel":13377},"https://cssdb.org/#relative-color-syntax",[34],[13379],{"type":27,"value":13366},{"type":27,"value":13381}," proposal which allows defining one color relative to another one.",{"type":22,"tag":115,"props":13383,"children":13384},{},[13385,13391,13393,13399],{"type":22,"tag":486,"props":13386,"children":13388},{"className":13387},[],[13389],{"type":27,"value":13390},"color-contrast()",{"type":27,"value":13392}," helps choose the color that contrasts the most, though it is an experimental ",{"type":22,"tag":30,"props":13394,"children":13396},{"href":13362,"rel":13395},[34],[13397],{"type":27,"value":13398},"Stage 1 proposal",{"type":27,"value":71},{"type":22,"tag":115,"props":13401,"children":13402},{},[13403,13408,13410,13416,13418,13424,13426,13432,13434,13441,13443,13448],{"type":22,"tag":2598,"props":13404,"children":13405},{},[13406],{"type":27,"value":13407},"Concatenating class names:",{"type":27,"value":13409}," Some preprocessors have a syntax where a nested block's selector can be concatenated with the parent's, e.g. to combine ",{"type":22,"tag":486,"props":13411,"children":13413},{"className":13412},[],[13414],{"type":27,"value":13415},".parent",{"type":27,"value":13417}," with ",{"type":22,"tag":486,"props":13419,"children":13421},{"className":13420},[],[13422],{"type":27,"value":13423},"__child",{"type":27,"value":13425}," to form a ",{"type":22,"tag":486,"props":13427,"children":13429},{"className":13428},[],[13430],{"type":27,"value":13431},".parent__child",{"type":27,"value":13433}," class in ",{"type":22,"tag":30,"props":13435,"children":13438},{"href":13436,"rel":13437},"https://getbem.com/introduction/",[34],[13439],{"type":27,"value":13440},"BEM",{"type":27,"value":13442}," convention rather than writing an un-nested ",{"type":22,"tag":486,"props":13444,"children":13446},{"className":13445},[],[13447],{"type":27,"value":13431},{"type":27,"value":13449}," selector. CSS doesn't provide this either, but it's a mild convenience that comes at the expense of some clarity.",{"type":22,"tag":23,"props":13451,"children":13452},{},[13453],{"type":27,"value":13454},"Overall, variables seem like the key feature that was missing from CSS when preprocessors first gained popularity, but they're widely-adopted now and enable a lot of code reuse possibilities. Nesting is also considered essential by many, which currently requires enabling one Stage 2 proposal via a PostCSS plugin. It may feel scary to enable a proposal whose stage implies \"relatively unstable and subject to change\", but nesting support has recently landed in Chrome and Safari, so it's looking more standardized than its stage would suggest. And after all, Sass, Less and Stylus all have a history of breaking changes, so a certain level of risk of future breaking changes is inherent to using any of these tools.",{"type":22,"tag":23,"props":13456,"children":13457},{},[13458],{"type":27,"value":13459},"In other words, with one PostCSS plugin, you can write plain CSS in 2023 whose clean maintainability rivals what's possible in Sass, Less or Stylus. And there are dozens of other plugins to reach for if your use case ends up calling for something specific (such as fancy color functions).",{"type":22,"tag":23,"props":13461,"children":13462},{},[13463],{"type":27,"value":13464},"With this being the case, there's no reason to stop using a preprocessor that's already integrated into your codebase; that would be a time-consuming and regression-prone migration, for little gain. But for a greenfield project in 2023, I really don't think it's worthwhile any longer to complicate a project with yet another language and build step. Why not keep things simple and stick to plain CSS?",{"title":8,"searchDepth":755,"depth":755,"links":13466},[],"content:phendry:2023-05-16:DoYouNeedACSSPreprocessor.md","phendry/2023-05-16/DoYouNeedACSSPreprocessor.md","phendry/2023-05-16/DoYouNeedACSSPreprocessor",{"user":774,"name":775},{"_path":13472,"_dir":13473,"_draft":7,"_partial":7,"_locale":8,"title":13474,"description":13475,"tags":13476,"image":13477,"publishDate":13473,"excerpt":13475,"body":13478,"_type":767,"_id":16828,"_source":769,"_file":16829,"_stem":16830,"_extension":772,"author":16831},"/phendry/2021-10-30/spotthevulnloopsandtermconditions","2021-10-30","Spot the Vulnerability: Loops and Terminating Conditions","In memory-unsafe languages like C, special care must be taken when copying untrusted data, particularly when copying it to another buffer. In this post, we'll spot and mitigate a past vulnerability in Linux's NTP daemon.",[2818,2819],"/phendry/2021-10-30/img/vulnerability-2.jpg",{"type":19,"children":13479,"toc":16821},[13480,13501,13507,13512,13525,13718,13766,13771,13777,13782,13787,13841,15470,15475,15481,15507,15541,15546,15851,15883,16062,16067,16199,16204,16441,16482,16495,16507,16513,16525,16772,16777,16783,16817],{"type":22,"tag":23,"props":13481,"children":13482},{},[13483,13484,13491,13493,13500],{"type":27,"value":8024},{"type":22,"tag":30,"props":13485,"children":13488},{"href":13486,"rel":13487},"https://www.abetterinternet.org/docs/memory-safety/",[34],[13489],{"type":27,"value":13490},"memory-unsafe",{"type":27,"value":13492}," languages like C, special care must be taken when copying untrusted data, particularly when copying it to another buffer. In this post, we'll spot and mitigate a past vulnerability in Linux's ",{"type":22,"tag":30,"props":13494,"children":13497},{"href":13495,"rel":13496},"https://linux.die.net/man/8/ntpd",[34],[13498],{"type":27,"value":13499},"NTP daemon",{"type":27,"value":71},{"type":22,"tag":193,"props":13502,"children":13504},{"id":13503},"background-buffer-overflows-and-loop-termination",[13505],{"type":27,"value":13506},"Background: Buffer Overflows and Loop Termination",{"type":22,"tag":23,"props":13508,"children":13509},{},[13510],{"type":27,"value":13511},"A buffer overflow occurs when code which intends to write data to a buffer inadvertently writes beyond that buffer into an adjacent memory location. At best this might just invalidate the program's state and cause a crash or other unintended behaviour, but a malicious exploitation of a buffer overflow can often lead to protected data being overwritten by an attacker, leading to denial-of-service or arbitrary code execution.",{"type":22,"tag":23,"props":13513,"children":13514},{},[13515,13517,13523],{"type":27,"value":13516},"A common cause for buffer overflows is a looping construct whose terminating conditions are either invalid or missing. The simplest example of this is a ",{"type":22,"tag":486,"props":13518,"children":13520},{"className":13519},[],[13521],{"type":27,"value":13522},"strcpy()",{"type":27,"value":13524},"-style function like the following, intended to copy a string between source and destination buffers:",{"type":22,"tag":886,"props":13526,"children":13530},{"className":13527,"code":13528,"language":13529,"meta":8,"style":8},"language-c shiki shiki-themes github-light github-dark","char * copy(char *dst, char *src) {\n    char *dst_start = dst;\n\n    while((*dst++ = *src++) != '\\0');\n\n    return dst_start;\n}\n","c",[13531],{"type":22,"tag":486,"props":13532,"children":13533},{"__ignoreMap":8},[13534,13590,13616,13623,13692,13699,13711],{"type":22,"tag":896,"props":13535,"children":13536},{"class":898,"line":899},[13537,13542,13547,13552,13556,13560,13564,13569,13573,13577,13581,13586],{"type":22,"tag":896,"props":13538,"children":13539},{"style":1691},[13540],{"type":27,"value":13541},"char",{"type":22,"tag":896,"props":13543,"children":13544},{"style":1691},[13545],{"type":27,"value":13546}," *",{"type":22,"tag":896,"props":13548,"children":13549},{"style":933},[13550],{"type":27,"value":13551}," copy",{"type":22,"tag":896,"props":13553,"children":13554},{"style":903},[13555],{"type":27,"value":2888},{"type":22,"tag":896,"props":13557,"children":13558},{"style":1691},[13559],{"type":27,"value":13541},{"type":22,"tag":896,"props":13561,"children":13562},{"style":1691},[13563],{"type":27,"value":13546},{"type":22,"tag":896,"props":13565,"children":13566},{"style":6025},[13567],{"type":27,"value":13568},"dst",{"type":22,"tag":896,"props":13570,"children":13571},{"style":903},[13572],{"type":27,"value":236},{"type":22,"tag":896,"props":13574,"children":13575},{"style":1691},[13576],{"type":27,"value":13541},{"type":22,"tag":896,"props":13578,"children":13579},{"style":1691},[13580],{"type":27,"value":13546},{"type":22,"tag":896,"props":13582,"children":13583},{"style":6025},[13584],{"type":27,"value":13585},"src",{"type":22,"tag":896,"props":13587,"children":13588},{"style":903},[13589],{"type":27,"value":6377},{"type":22,"tag":896,"props":13591,"children":13592},{"class":898,"line":758},[13593,13598,13602,13607,13611],{"type":22,"tag":896,"props":13594,"children":13595},{"style":1691},[13596],{"type":27,"value":13597},"    char",{"type":22,"tag":896,"props":13599,"children":13600},{"style":1691},[13601],{"type":27,"value":13546},{"type":22,"tag":896,"props":13603,"children":13604},{"style":903},[13605],{"type":27,"value":13606},"dst_start ",{"type":22,"tag":896,"props":13608,"children":13609},{"style":1691},[13610],{"type":27,"value":941},{"type":22,"tag":896,"props":13612,"children":13613},{"style":903},[13614],{"type":27,"value":13615}," dst;\n",{"type":22,"tag":896,"props":13617,"children":13618},{"class":898,"line":755},[13619],{"type":22,"tag":896,"props":13620,"children":13621},{"emptyLinePlaceholder":3481},[13622],{"type":27,"value":3484},{"type":22,"tag":896,"props":13624,"children":13625},{"class":898,"line":983},[13626,13631,13635,13639,13643,13648,13652,13656,13660,13664,13668,13673,13678,13683,13688],{"type":22,"tag":896,"props":13627,"children":13628},{"style":1691},[13629],{"type":27,"value":13630},"    while",{"type":22,"tag":896,"props":13632,"children":13633},{"style":903},[13634],{"type":27,"value":6073},{"type":22,"tag":896,"props":13636,"children":13637},{"style":1691},[13638],{"type":27,"value":3668},{"type":22,"tag":896,"props":13640,"children":13641},{"style":903},[13642],{"type":27,"value":13568},{"type":22,"tag":896,"props":13644,"children":13645},{"style":1691},[13646],{"type":27,"value":13647},"++",{"type":22,"tag":896,"props":13649,"children":13650},{"style":1691},[13651],{"type":27,"value":3638},{"type":22,"tag":896,"props":13653,"children":13654},{"style":1691},[13655],{"type":27,"value":13546},{"type":22,"tag":896,"props":13657,"children":13658},{"style":903},[13659],{"type":27,"value":13585},{"type":22,"tag":896,"props":13661,"children":13662},{"style":1691},[13663],{"type":27,"value":13647},{"type":22,"tag":896,"props":13665,"children":13666},{"style":903},[13667],{"type":27,"value":6082},{"type":22,"tag":896,"props":13669,"children":13670},{"style":1691},[13671],{"type":27,"value":13672},"!=",{"type":22,"tag":896,"props":13674,"children":13675},{"style":944},[13676],{"type":27,"value":13677}," '",{"type":22,"tag":896,"props":13679,"children":13680},{"style":1632},[13681],{"type":27,"value":13682},"\\0",{"type":22,"tag":896,"props":13684,"children":13685},{"style":944},[13686],{"type":27,"value":13687},"'",{"type":22,"tag":896,"props":13689,"children":13690},{"style":903},[13691],{"type":27,"value":3678},{"type":22,"tag":896,"props":13693,"children":13694},{"class":898,"line":1013},[13695],{"type":22,"tag":896,"props":13696,"children":13697},{"emptyLinePlaceholder":3481},[13698],{"type":27,"value":3484},{"type":22,"tag":896,"props":13700,"children":13701},{"class":898,"line":1030},[13702,13706],{"type":22,"tag":896,"props":13703,"children":13704},{"style":1691},[13705],{"type":27,"value":3493},{"type":22,"tag":896,"props":13707,"children":13708},{"style":903},[13709],{"type":27,"value":13710}," dst_start;\n",{"type":22,"tag":896,"props":13712,"children":13713},{"class":898,"line":1068},[13714],{"type":22,"tag":896,"props":13715,"children":13716},{"style":903},[13717],{"type":27,"value":1745},{"type":22,"tag":23,"props":13719,"children":13720},{},[13721,13723,13728,13730,13735,13737,13743,13745,13750,13752,13757,13759,13764],{"type":27,"value":13722},"The above loop copies from ",{"type":22,"tag":486,"props":13724,"children":13726},{"className":13725},[],[13727],{"type":27,"value":13585},{"type":27,"value":13729}," to ",{"type":22,"tag":486,"props":13731,"children":13733},{"className":13732},[],[13734],{"type":27,"value":13568},{"type":27,"value":13736}," until it sees a ",{"type":22,"tag":486,"props":13738,"children":13740},{"className":13739},[],[13741],{"type":27,"value":13742},"NULL",{"type":27,"value":13744}," character, with no bearing on the size of the ",{"type":22,"tag":486,"props":13746,"children":13748},{"className":13747},[],[13749],{"type":27,"value":13568},{"type":27,"value":13751}," buffer. If ",{"type":22,"tag":486,"props":13753,"children":13755},{"className":13754},[],[13756],{"type":27,"value":13585},{"type":27,"value":13758}," is larger than ",{"type":22,"tag":486,"props":13760,"children":13762},{"className":13761},[],[13763],{"type":27,"value":13568},{"type":27,"value":13765},", this will overrun the buffer.",{"type":22,"tag":23,"props":13767,"children":13768},{},[13769],{"type":27,"value":13770},"While the buffer overflow potential of the above is easy to recognize, it can be much more difficult when complex application logic is mixed in.",{"type":22,"tag":193,"props":13772,"children":13774},{"id":13773},"processing-a-packet-in-ntpd",[13775],{"type":27,"value":13776},"Processing a Packet in NTPD",{"type":22,"tag":23,"props":13778,"children":13779},{},[13780],{"type":27,"value":13781},"What follows is more-or-less exactly the vulnerable NTPD code, presented in its entirety to help demonstrate how tricky it can be to assess complex loops for correctness.",{"type":22,"tag":23,"props":13783,"children":13784},{},[13785],{"type":27,"value":13786},"In the code below,",{"type":22,"tag":111,"props":13788,"children":13789},{},[13790,13801,13812,13823],{"type":22,"tag":115,"props":13791,"children":13792},{},[13793,13799],{"type":22,"tag":486,"props":13794,"children":13796},{"className":13795},[],[13797],{"type":27,"value":13798},"ctl_getitem()",{"type":27,"value":13800}," is a function which gets the next data item from an incoming NTP packet,",{"type":22,"tag":115,"props":13802,"children":13803},{},[13804,13810],{"type":22,"tag":486,"props":13805,"children":13807},{"className":13806},[],[13808],{"type":27,"value":13809},"buf",{"type":27,"value":13811}," is the 128-character buffer into which packet data is written,",{"type":22,"tag":115,"props":13813,"children":13814},{},[13815,13821],{"type":22,"tag":486,"props":13816,"children":13818},{"className":13817},[],[13819],{"type":27,"value":13820},"cp",{"type":27,"value":13822}," is a pointer into the input data (used during the copy), and",{"type":22,"tag":115,"props":13824,"children":13825},{},[13826,13832,13834,13839],{"type":22,"tag":486,"props":13827,"children":13829},{"className":13828},[],[13830],{"type":27,"value":13831},"tp",{"type":27,"value":13833}," is a pointer into ",{"type":22,"tag":486,"props":13835,"children":13837},{"className":13836},[],[13838],{"type":27,"value":13809},{"type":27,"value":13840}," (also used during the copy).",{"type":22,"tag":886,"props":13842,"children":13844},{"className":13527,"code":13843,"language":13529,"meta":8,"style":8},"static struct ctl_var *\nctl_getitem(\n    struct ctl_var *var_list,\n    char **data\n)\n{\n    register struct ctl_var *v;\n    register char *cp;\n    register char *tp;\n    static struct ctl_var eol = { 0, EOV, };\n    static char buf[128];\n\n    /*\n     * Delete leading commas and white space\n     */\n    while (reqpt \u003C reqend && (*reqpt == ',' || isspace((int)*reqpt))) {\n        reqpt++;\n    }\n\n    if (reqpt >= reqend)\n        return 0;\n\n    if (var_list == (struct c tl_var *)0)\n        return &eol;\n\n    /*\n     * Look for a first character match on the tag. If we find one, see if it is a full match.\n     */\n    v = var_list;\n    cp = reqpt;\n    while (!(v->flags & EOV)) {\n        if (!(v->flags & PADDING) && *cp == *(v->text)) {\n            tp = v->text;\n            while (*tp != '\\0' && *tp != '=' && cp \u003C reqend && *cp == *tp) {\n                cp++;\n                tp++;\n            }\n\n            if ((*tp == '\\0') || (*tp == '=')) {\n                while (cp \u003C reqend && isspace((int)*cp))\n                    cp++;\n\n                if (cp == reqend || *cp == ',') {\n                    buf[0] = '\\0';\n                    *data = buf;\n                    if (cp \u003C reqend)\n                        cp++;\n                    reqpt = cp;\n                    return v;\n                }\n\n                if (*cp == '=') {\n                    cp++;\n                    tp = buf;\n                    while (cp \u003C reqend && isspace((int)*cp))\n                        cp++;\n\n                    while (cp \u003C reqend && *cp != ',')\n                        *tp++ = *cp++;\n                    if (cp \u003C reqend)\n                        cp++;\n                    *tp = '\\0';\n                    while (isspace((int)(*(tp - 1))))\n                        *(--tp) = '\\0';\n\n                    reqpt = cp;\n                    *data = buf;\n                    return v;\n                }\n            }\n            cp = reqpt;\n        }\n        v++;\n    }\n    return v;\n}\n",[13845],{"type":22,"tag":486,"props":13846,"children":13847},{"__ignoreMap":8},[13848,13871,13884,13909,13926,13933,13940,13965,13986,14006,14041,14072,14079,14087,14095,14103,14182,14198,14205,14212,14233,14248,14255,14301,14318,14325,14332,14340,14347,14364,14381,14411,14465,14482,14582,14598,14614,14622,14629,14700,14750,14766,14773,14817,14857,14879,14899,14915,14932,14945,14953,14960,14991,15006,15022,15070,15085,15092,15135,15171,15190,15205,15236,15286,15328,15336,15352,15372,15384,15392,15400,15417,15425,15442,15450,15462],{"type":22,"tag":896,"props":13849,"children":13850},{"class":898,"line":899},[13851,13856,13861,13866],{"type":22,"tag":896,"props":13852,"children":13853},{"style":1691},[13854],{"type":27,"value":13855},"static",{"type":22,"tag":896,"props":13857,"children":13858},{"style":1691},[13859],{"type":27,"value":13860}," struct",{"type":22,"tag":896,"props":13862,"children":13863},{"style":903},[13864],{"type":27,"value":13865}," ctl_var ",{"type":22,"tag":896,"props":13867,"children":13868},{"style":1691},[13869],{"type":27,"value":13870},"*\n",{"type":22,"tag":896,"props":13872,"children":13873},{"class":898,"line":758},[13874,13879],{"type":22,"tag":896,"props":13875,"children":13876},{"style":933},[13877],{"type":27,"value":13878},"ctl_getitem",{"type":22,"tag":896,"props":13880,"children":13881},{"style":903},[13882],{"type":27,"value":13883},"(\n",{"type":22,"tag":896,"props":13885,"children":13886},{"class":898,"line":755},[13887,13892,13896,13900,13905],{"type":22,"tag":896,"props":13888,"children":13889},{"style":1691},[13890],{"type":27,"value":13891},"    struct",{"type":22,"tag":896,"props":13893,"children":13894},{"style":903},[13895],{"type":27,"value":13865},{"type":22,"tag":896,"props":13897,"children":13898},{"style":1691},[13899],{"type":27,"value":3668},{"type":22,"tag":896,"props":13901,"children":13902},{"style":6025},[13903],{"type":27,"value":13904},"var_list",{"type":22,"tag":896,"props":13906,"children":13907},{"style":903},[13908],{"type":27,"value":3988},{"type":22,"tag":896,"props":13910,"children":13911},{"class":898,"line":983},[13912,13916,13921],{"type":22,"tag":896,"props":13913,"children":13914},{"style":1691},[13915],{"type":27,"value":13597},{"type":22,"tag":896,"props":13917,"children":13918},{"style":1691},[13919],{"type":27,"value":13920}," **",{"type":22,"tag":896,"props":13922,"children":13923},{"style":903},[13924],{"type":27,"value":13925},"data\n",{"type":22,"tag":896,"props":13927,"children":13928},{"class":898,"line":1013},[13929],{"type":22,"tag":896,"props":13930,"children":13931},{"style":903},[13932],{"type":27,"value":2903},{"type":22,"tag":896,"props":13934,"children":13935},{"class":898,"line":1030},[13936],{"type":22,"tag":896,"props":13937,"children":13938},{"style":903},[13939],{"type":27,"value":2571},{"type":22,"tag":896,"props":13941,"children":13942},{"class":898,"line":1068},[13943,13948,13952,13956,13960],{"type":22,"tag":896,"props":13944,"children":13945},{"style":1691},[13946],{"type":27,"value":13947},"    register",{"type":22,"tag":896,"props":13949,"children":13950},{"style":1691},[13951],{"type":27,"value":13860},{"type":22,"tag":896,"props":13953,"children":13954},{"style":903},[13955],{"type":27,"value":13865},{"type":22,"tag":896,"props":13957,"children":13958},{"style":1691},[13959],{"type":27,"value":3668},{"type":22,"tag":896,"props":13961,"children":13962},{"style":903},[13963],{"type":27,"value":13964},"v;\n",{"type":22,"tag":896,"props":13966,"children":13967},{"class":898,"line":1085},[13968,13972,13977,13981],{"type":22,"tag":896,"props":13969,"children":13970},{"style":1691},[13971],{"type":27,"value":13947},{"type":22,"tag":896,"props":13973,"children":13974},{"style":1691},[13975],{"type":27,"value":13976}," char",{"type":22,"tag":896,"props":13978,"children":13979},{"style":1691},[13980],{"type":27,"value":13546},{"type":22,"tag":896,"props":13982,"children":13983},{"style":903},[13984],{"type":27,"value":13985},"cp;\n",{"type":22,"tag":896,"props":13987,"children":13988},{"class":898,"line":1123},[13989,13993,13997,14001],{"type":22,"tag":896,"props":13990,"children":13991},{"style":1691},[13992],{"type":27,"value":13947},{"type":22,"tag":896,"props":13994,"children":13995},{"style":1691},[13996],{"type":27,"value":13976},{"type":22,"tag":896,"props":13998,"children":13999},{"style":1691},[14000],{"type":27,"value":13546},{"type":22,"tag":896,"props":14002,"children":14003},{"style":903},[14004],{"type":27,"value":14005},"tp;\n",{"type":22,"tag":896,"props":14007,"children":14008},{"class":898,"line":1161},[14009,14014,14018,14023,14027,14032,14036],{"type":22,"tag":896,"props":14010,"children":14011},{"style":1691},[14012],{"type":27,"value":14013},"    static",{"type":22,"tag":896,"props":14015,"children":14016},{"style":1691},[14017],{"type":27,"value":13860},{"type":22,"tag":896,"props":14019,"children":14020},{"style":903},[14021],{"type":27,"value":14022}," ctl_var eol ",{"type":22,"tag":896,"props":14024,"children":14025},{"style":1691},[14026],{"type":27,"value":941},{"type":22,"tag":896,"props":14028,"children":14029},{"style":903},[14030],{"type":27,"value":14031}," { ",{"type":22,"tag":896,"props":14033,"children":14034},{"style":1632},[14035],{"type":27,"value":9362},{"type":22,"tag":896,"props":14037,"children":14038},{"style":903},[14039],{"type":27,"value":14040},", EOV, };\n",{"type":22,"tag":896,"props":14042,"children":14043},{"class":898,"line":2298},[14044,14048,14052,14057,14062,14067],{"type":22,"tag":896,"props":14045,"children":14046},{"style":1691},[14047],{"type":27,"value":14013},{"type":22,"tag":896,"props":14049,"children":14050},{"style":1691},[14051],{"type":27,"value":13976},{"type":22,"tag":896,"props":14053,"children":14054},{"style":6025},[14055],{"type":27,"value":14056}," buf",{"type":22,"tag":896,"props":14058,"children":14059},{"style":903},[14060],{"type":27,"value":14061},"[",{"type":22,"tag":896,"props":14063,"children":14064},{"style":1632},[14065],{"type":27,"value":14066},"128",{"type":22,"tag":896,"props":14068,"children":14069},{"style":903},[14070],{"type":27,"value":14071},"];\n",{"type":22,"tag":896,"props":14073,"children":14074},{"class":898,"line":3170},[14075],{"type":22,"tag":896,"props":14076,"children":14077},{"emptyLinePlaceholder":3481},[14078],{"type":27,"value":3484},{"type":22,"tag":896,"props":14080,"children":14081},{"class":898,"line":3187},[14082],{"type":22,"tag":896,"props":14083,"children":14084},{"style":2070},[14085],{"type":27,"value":14086},"    /*\n",{"type":22,"tag":896,"props":14088,"children":14089},{"class":898,"line":3228},[14090],{"type":22,"tag":896,"props":14091,"children":14092},{"style":2070},[14093],{"type":27,"value":14094},"     * Delete leading commas and white space\n",{"type":22,"tag":896,"props":14096,"children":14097},{"class":898,"line":3245},[14098],{"type":22,"tag":896,"props":14099,"children":14100},{"style":2070},[14101],{"type":27,"value":14102},"     */\n",{"type":22,"tag":896,"props":14104,"children":14105},{"class":898,"line":3286},[14106,14110,14115,14119,14124,14129,14133,14137,14142,14146,14151,14155,14160,14164,14168,14173,14177],{"type":22,"tag":896,"props":14107,"children":14108},{"style":1691},[14109],{"type":27,"value":13630},{"type":22,"tag":896,"props":14111,"children":14112},{"style":903},[14113],{"type":27,"value":14114}," (reqpt ",{"type":22,"tag":896,"props":14116,"children":14117},{"style":1691},[14118],{"type":27,"value":906},{"type":22,"tag":896,"props":14120,"children":14121},{"style":903},[14122],{"type":27,"value":14123}," reqend ",{"type":22,"tag":896,"props":14125,"children":14126},{"style":1691},[14127],{"type":27,"value":14128},"&&",{"type":22,"tag":896,"props":14130,"children":14131},{"style":903},[14132],{"type":27,"value":3643},{"type":22,"tag":896,"props":14134,"children":14135},{"style":1691},[14136],{"type":27,"value":3668},{"type":22,"tag":896,"props":14138,"children":14139},{"style":903},[14140],{"type":27,"value":14141},"reqpt ",{"type":22,"tag":896,"props":14143,"children":14144},{"style":1691},[14145],{"type":27,"value":2928},{"type":22,"tag":896,"props":14147,"children":14148},{"style":944},[14149],{"type":27,"value":14150}," ','",{"type":22,"tag":896,"props":14152,"children":14153},{"style":1691},[14154],{"type":27,"value":5381},{"type":22,"tag":896,"props":14156,"children":14157},{"style":933},[14158],{"type":27,"value":14159}," isspace",{"type":22,"tag":896,"props":14161,"children":14162},{"style":903},[14163],{"type":27,"value":6073},{"type":22,"tag":896,"props":14165,"children":14166},{"style":1691},[14167],{"type":27,"value":3648},{"type":22,"tag":896,"props":14169,"children":14170},{"style":903},[14171],{"type":27,"value":14172},")",{"type":22,"tag":896,"props":14174,"children":14175},{"style":1691},[14176],{"type":27,"value":3668},{"type":22,"tag":896,"props":14178,"children":14179},{"style":903},[14180],{"type":27,"value":14181},"reqpt))) {\n",{"type":22,"tag":896,"props":14183,"children":14184},{"class":898,"line":3303},[14185,14190,14194],{"type":22,"tag":896,"props":14186,"children":14187},{"style":903},[14188],{"type":27,"value":14189},"        reqpt",{"type":22,"tag":896,"props":14191,"children":14192},{"style":1691},[14193],{"type":27,"value":13647},{"type":22,"tag":896,"props":14195,"children":14196},{"style":903},[14197],{"type":27,"value":1649},{"type":22,"tag":896,"props":14199,"children":14200},{"class":898,"line":3344},[14201],{"type":22,"tag":896,"props":14202,"children":14203},{"style":903},[14204],{"type":27,"value":7506},{"type":22,"tag":896,"props":14206,"children":14207},{"class":898,"line":3361},[14208],{"type":22,"tag":896,"props":14209,"children":14210},{"emptyLinePlaceholder":3481},[14211],{"type":27,"value":3484},{"type":22,"tag":896,"props":14213,"children":14214},{"class":898,"line":3402},[14215,14219,14223,14228],{"type":22,"tag":896,"props":14216,"children":14217},{"style":1691},[14218],{"type":27,"value":2918},{"type":22,"tag":896,"props":14220,"children":14221},{"style":903},[14222],{"type":27,"value":14114},{"type":22,"tag":896,"props":14224,"children":14225},{"style":1691},[14226],{"type":27,"value":14227},">=",{"type":22,"tag":896,"props":14229,"children":14230},{"style":903},[14231],{"type":27,"value":14232}," reqend)\n",{"type":22,"tag":896,"props":14234,"children":14235},{"class":898,"line":3419},[14236,14240,14244],{"type":22,"tag":896,"props":14237,"children":14238},{"style":1691},[14239],{"type":27,"value":2945},{"type":22,"tag":896,"props":14241,"children":14242},{"style":1632},[14243],{"type":27,"value":2933},{"type":22,"tag":896,"props":14245,"children":14246},{"style":903},[14247],{"type":27,"value":1649},{"type":22,"tag":896,"props":14249,"children":14250},{"class":898,"line":3460},[14251],{"type":22,"tag":896,"props":14252,"children":14253},{"emptyLinePlaceholder":3481},[14254],{"type":27,"value":3484},{"type":22,"tag":896,"props":14256,"children":14257},{"class":898,"line":3477},[14258,14262,14267,14271,14275,14280,14285,14289,14293,14297],{"type":22,"tag":896,"props":14259,"children":14260},{"style":1691},[14261],{"type":27,"value":2918},{"type":22,"tag":896,"props":14263,"children":14264},{"style":903},[14265],{"type":27,"value":14266}," (var_list ",{"type":22,"tag":896,"props":14268,"children":14269},{"style":1691},[14270],{"type":27,"value":2928},{"type":22,"tag":896,"props":14272,"children":14273},{"style":903},[14274],{"type":27,"value":3643},{"type":22,"tag":896,"props":14276,"children":14277},{"style":1691},[14278],{"type":27,"value":14279},"struct",{"type":22,"tag":896,"props":14281,"children":14282},{"style":903},[14283],{"type":27,"value":14284}," c tl_var ",{"type":22,"tag":896,"props":14286,"children":14287},{"style":1691},[14288],{"type":27,"value":3668},{"type":22,"tag":896,"props":14290,"children":14291},{"style":903},[14292],{"type":27,"value":14172},{"type":22,"tag":896,"props":14294,"children":14295},{"style":1632},[14296],{"type":27,"value":9362},{"type":22,"tag":896,"props":14298,"children":14299},{"style":903},[14300],{"type":27,"value":2903},{"type":22,"tag":896,"props":14302,"children":14303},{"class":898,"line":3487},[14304,14308,14313],{"type":22,"tag":896,"props":14305,"children":14306},{"style":1691},[14307],{"type":27,"value":2945},{"type":22,"tag":896,"props":14309,"children":14310},{"style":1691},[14311],{"type":27,"value":14312}," &",{"type":22,"tag":896,"props":14314,"children":14315},{"style":903},[14316],{"type":27,"value":14317},"eol;\n",{"type":22,"tag":896,"props":14319,"children":14320},{"class":898,"line":3505},[14321],{"type":22,"tag":896,"props":14322,"children":14323},{"emptyLinePlaceholder":3481},[14324],{"type":27,"value":3484},{"type":22,"tag":896,"props":14326,"children":14327},{"class":898,"line":5985},[14328],{"type":22,"tag":896,"props":14329,"children":14330},{"style":2070},[14331],{"type":27,"value":14086},{"type":22,"tag":896,"props":14333,"children":14334},{"class":898,"line":5993},[14335],{"type":22,"tag":896,"props":14336,"children":14337},{"style":2070},[14338],{"type":27,"value":14339},"     * Look for a first character match on the tag. If we find one, see if it is a full match.\n",{"type":22,"tag":896,"props":14341,"children":14342},{"class":898,"line":6007},[14343],{"type":22,"tag":896,"props":14344,"children":14345},{"style":2070},[14346],{"type":27,"value":14102},{"type":22,"tag":896,"props":14348,"children":14349},{"class":898,"line":6059},[14350,14355,14359],{"type":22,"tag":896,"props":14351,"children":14352},{"style":903},[14353],{"type":27,"value":14354},"    v ",{"type":22,"tag":896,"props":14356,"children":14357},{"style":1691},[14358],{"type":27,"value":941},{"type":22,"tag":896,"props":14360,"children":14361},{"style":903},[14362],{"type":27,"value":14363}," var_list;\n",{"type":22,"tag":896,"props":14365,"children":14366},{"class":898,"line":6094},[14367,14372,14376],{"type":22,"tag":896,"props":14368,"children":14369},{"style":903},[14370],{"type":27,"value":14371},"    cp ",{"type":22,"tag":896,"props":14373,"children":14374},{"style":1691},[14375],{"type":27,"value":941},{"type":22,"tag":896,"props":14377,"children":14378},{"style":903},[14379],{"type":27,"value":14380}," reqpt;\n",{"type":22,"tag":896,"props":14382,"children":14383},{"class":898,"line":6125},[14384,14388,14392,14396,14401,14406],{"type":22,"tag":896,"props":14385,"children":14386},{"style":1691},[14387],{"type":27,"value":13630},{"type":22,"tag":896,"props":14389,"children":14390},{"style":903},[14391],{"type":27,"value":3643},{"type":22,"tag":896,"props":14393,"children":14394},{"style":1691},[14395],{"type":27,"value":7247},{"type":22,"tag":896,"props":14397,"children":14398},{"style":903},[14399],{"type":27,"value":14400},"(v->flags ",{"type":22,"tag":896,"props":14402,"children":14403},{"style":1691},[14404],{"type":27,"value":14405},"&",{"type":22,"tag":896,"props":14407,"children":14408},{"style":903},[14409],{"type":27,"value":14410}," EOV)) {\n",{"type":22,"tag":896,"props":14412,"children":14413},{"class":898,"line":7440},[14414,14418,14422,14426,14430,14434,14439,14443,14447,14452,14456,14460],{"type":22,"tag":896,"props":14415,"children":14416},{"style":1691},[14417],{"type":27,"value":7124},{"type":22,"tag":896,"props":14419,"children":14420},{"style":903},[14421],{"type":27,"value":3643},{"type":22,"tag":896,"props":14423,"children":14424},{"style":1691},[14425],{"type":27,"value":7247},{"type":22,"tag":896,"props":14427,"children":14428},{"style":903},[14429],{"type":27,"value":14400},{"type":22,"tag":896,"props":14431,"children":14432},{"style":1691},[14433],{"type":27,"value":14405},{"type":22,"tag":896,"props":14435,"children":14436},{"style":903},[14437],{"type":27,"value":14438}," PADDING) ",{"type":22,"tag":896,"props":14440,"children":14441},{"style":1691},[14442],{"type":27,"value":14128},{"type":22,"tag":896,"props":14444,"children":14445},{"style":1691},[14446],{"type":27,"value":13546},{"type":22,"tag":896,"props":14448,"children":14449},{"style":903},[14450],{"type":27,"value":14451},"cp ",{"type":22,"tag":896,"props":14453,"children":14454},{"style":1691},[14455],{"type":27,"value":2928},{"type":22,"tag":896,"props":14457,"children":14458},{"style":1691},[14459],{"type":27,"value":13546},{"type":22,"tag":896,"props":14461,"children":14462},{"style":903},[14463],{"type":27,"value":14464},"(v->text)) {\n",{"type":22,"tag":896,"props":14466,"children":14467},{"class":898,"line":7463},[14468,14473,14477],{"type":22,"tag":896,"props":14469,"children":14470},{"style":903},[14471],{"type":27,"value":14472},"            tp ",{"type":22,"tag":896,"props":14474,"children":14475},{"style":1691},[14476],{"type":27,"value":941},{"type":22,"tag":896,"props":14478,"children":14479},{"style":903},[14480],{"type":27,"value":14481}," v->text;\n",{"type":22,"tag":896,"props":14483,"children":14484},{"class":898,"line":7481},[14485,14490,14494,14498,14503,14507,14511,14515,14519,14523,14527,14531,14535,14540,14544,14549,14553,14557,14561,14565,14569,14573,14577],{"type":22,"tag":896,"props":14486,"children":14487},{"style":1691},[14488],{"type":27,"value":14489},"            while",{"type":22,"tag":896,"props":14491,"children":14492},{"style":903},[14493],{"type":27,"value":3643},{"type":22,"tag":896,"props":14495,"children":14496},{"style":1691},[14497],{"type":27,"value":3668},{"type":22,"tag":896,"props":14499,"children":14500},{"style":903},[14501],{"type":27,"value":14502},"tp ",{"type":22,"tag":896,"props":14504,"children":14505},{"style":1691},[14506],{"type":27,"value":13672},{"type":22,"tag":896,"props":14508,"children":14509},{"style":944},[14510],{"type":27,"value":13677},{"type":22,"tag":896,"props":14512,"children":14513},{"style":1632},[14514],{"type":27,"value":13682},{"type":22,"tag":896,"props":14516,"children":14517},{"style":944},[14518],{"type":27,"value":13687},{"type":22,"tag":896,"props":14520,"children":14521},{"style":1691},[14522],{"type":27,"value":2980},{"type":22,"tag":896,"props":14524,"children":14525},{"style":1691},[14526],{"type":27,"value":13546},{"type":22,"tag":896,"props":14528,"children":14529},{"style":903},[14530],{"type":27,"value":14502},{"type":22,"tag":896,"props":14532,"children":14533},{"style":1691},[14534],{"type":27,"value":13672},{"type":22,"tag":896,"props":14536,"children":14537},{"style":944},[14538],{"type":27,"value":14539}," '='",{"type":22,"tag":896,"props":14541,"children":14542},{"style":1691},[14543],{"type":27,"value":2980},{"type":22,"tag":896,"props":14545,"children":14546},{"style":903},[14547],{"type":27,"value":14548}," cp ",{"type":22,"tag":896,"props":14550,"children":14551},{"style":1691},[14552],{"type":27,"value":906},{"type":22,"tag":896,"props":14554,"children":14555},{"style":903},[14556],{"type":27,"value":14123},{"type":22,"tag":896,"props":14558,"children":14559},{"style":1691},[14560],{"type":27,"value":14128},{"type":22,"tag":896,"props":14562,"children":14563},{"style":1691},[14564],{"type":27,"value":13546},{"type":22,"tag":896,"props":14566,"children":14567},{"style":903},[14568],{"type":27,"value":14451},{"type":22,"tag":896,"props":14570,"children":14571},{"style":1691},[14572],{"type":27,"value":2928},{"type":22,"tag":896,"props":14574,"children":14575},{"style":1691},[14576],{"type":27,"value":13546},{"type":22,"tag":896,"props":14578,"children":14579},{"style":903},[14580],{"type":27,"value":14581},"tp) {\n",{"type":22,"tag":896,"props":14583,"children":14584},{"class":898,"line":7500},[14585,14590,14594],{"type":22,"tag":896,"props":14586,"children":14587},{"style":903},[14588],{"type":27,"value":14589},"                cp",{"type":22,"tag":896,"props":14591,"children":14592},{"style":1691},[14593],{"type":27,"value":13647},{"type":22,"tag":896,"props":14595,"children":14596},{"style":903},[14597],{"type":27,"value":1649},{"type":22,"tag":896,"props":14599,"children":14600},{"class":898,"line":7509},[14601,14606,14610],{"type":22,"tag":896,"props":14602,"children":14603},{"style":903},[14604],{"type":27,"value":14605},"                tp",{"type":22,"tag":896,"props":14607,"children":14608},{"style":1691},[14609],{"type":27,"value":13647},{"type":22,"tag":896,"props":14611,"children":14612},{"style":903},[14613],{"type":27,"value":1649},{"type":22,"tag":896,"props":14615,"children":14616},{"class":898,"line":7517},[14617],{"type":22,"tag":896,"props":14618,"children":14619},{"style":903},[14620],{"type":27,"value":14621},"            }\n",{"type":22,"tag":896,"props":14623,"children":14624},{"class":898,"line":7526},[14625],{"type":22,"tag":896,"props":14626,"children":14627},{"emptyLinePlaceholder":3481},[14628],{"type":27,"value":3484},{"type":22,"tag":896,"props":14630,"children":14631},{"class":898,"line":7583},[14632,14637,14642,14646,14650,14654,14658,14662,14666,14670,14675,14679,14683,14687,14691,14695],{"type":22,"tag":896,"props":14633,"children":14634},{"style":1691},[14635],{"type":27,"value":14636},"            if",{"type":22,"tag":896,"props":14638,"children":14639},{"style":903},[14640],{"type":27,"value":14641}," ((",{"type":22,"tag":896,"props":14643,"children":14644},{"style":1691},[14645],{"type":27,"value":3668},{"type":22,"tag":896,"props":14647,"children":14648},{"style":903},[14649],{"type":27,"value":14502},{"type":22,"tag":896,"props":14651,"children":14652},{"style":1691},[14653],{"type":27,"value":2928},{"type":22,"tag":896,"props":14655,"children":14656},{"style":944},[14657],{"type":27,"value":13677},{"type":22,"tag":896,"props":14659,"children":14660},{"style":1632},[14661],{"type":27,"value":13682},{"type":22,"tag":896,"props":14663,"children":14664},{"style":944},[14665],{"type":27,"value":13687},{"type":22,"tag":896,"props":14667,"children":14668},{"style":903},[14669],{"type":27,"value":6082},{"type":22,"tag":896,"props":14671,"children":14672},{"style":1691},[14673],{"type":27,"value":14674},"||",{"type":22,"tag":896,"props":14676,"children":14677},{"style":903},[14678],{"type":27,"value":3643},{"type":22,"tag":896,"props":14680,"children":14681},{"style":1691},[14682],{"type":27,"value":3668},{"type":22,"tag":896,"props":14684,"children":14685},{"style":903},[14686],{"type":27,"value":14502},{"type":22,"tag":896,"props":14688,"children":14689},{"style":1691},[14690],{"type":27,"value":2928},{"type":22,"tag":896,"props":14692,"children":14693},{"style":944},[14694],{"type":27,"value":14539},{"type":22,"tag":896,"props":14696,"children":14697},{"style":903},[14698],{"type":27,"value":14699},")) {\n",{"type":22,"tag":896,"props":14701,"children":14702},{"class":898,"line":7595},[14703,14708,14713,14717,14721,14725,14729,14733,14737,14741,14745],{"type":22,"tag":896,"props":14704,"children":14705},{"style":1691},[14706],{"type":27,"value":14707},"                while",{"type":22,"tag":896,"props":14709,"children":14710},{"style":903},[14711],{"type":27,"value":14712}," (cp ",{"type":22,"tag":896,"props":14714,"children":14715},{"style":1691},[14716],{"type":27,"value":906},{"type":22,"tag":896,"props":14718,"children":14719},{"style":903},[14720],{"type":27,"value":14123},{"type":22,"tag":896,"props":14722,"children":14723},{"style":1691},[14724],{"type":27,"value":14128},{"type":22,"tag":896,"props":14726,"children":14727},{"style":933},[14728],{"type":27,"value":14159},{"type":22,"tag":896,"props":14730,"children":14731},{"style":903},[14732],{"type":27,"value":6073},{"type":22,"tag":896,"props":14734,"children":14735},{"style":1691},[14736],{"type":27,"value":3648},{"type":22,"tag":896,"props":14738,"children":14739},{"style":903},[14740],{"type":27,"value":14172},{"type":22,"tag":896,"props":14742,"children":14743},{"style":1691},[14744],{"type":27,"value":3668},{"type":22,"tag":896,"props":14746,"children":14747},{"style":903},[14748],{"type":27,"value":14749},"cp))\n",{"type":22,"tag":896,"props":14751,"children":14752},{"class":898,"line":7615},[14753,14758,14762],{"type":22,"tag":896,"props":14754,"children":14755},{"style":903},[14756],{"type":27,"value":14757},"                    cp",{"type":22,"tag":896,"props":14759,"children":14760},{"style":1691},[14761],{"type":27,"value":13647},{"type":22,"tag":896,"props":14763,"children":14764},{"style":903},[14765],{"type":27,"value":1649},{"type":22,"tag":896,"props":14767,"children":14768},{"class":898,"line":7632},[14769],{"type":22,"tag":896,"props":14770,"children":14771},{"emptyLinePlaceholder":3481},[14772],{"type":27,"value":3484},{"type":22,"tag":896,"props":14774,"children":14775},{"class":898,"line":7640},[14776,14781,14785,14789,14793,14797,14801,14805,14809,14813],{"type":22,"tag":896,"props":14777,"children":14778},{"style":1691},[14779],{"type":27,"value":14780},"                if",{"type":22,"tag":896,"props":14782,"children":14783},{"style":903},[14784],{"type":27,"value":14712},{"type":22,"tag":896,"props":14786,"children":14787},{"style":1691},[14788],{"type":27,"value":2928},{"type":22,"tag":896,"props":14790,"children":14791},{"style":903},[14792],{"type":27,"value":14123},{"type":22,"tag":896,"props":14794,"children":14795},{"style":1691},[14796],{"type":27,"value":14674},{"type":22,"tag":896,"props":14798,"children":14799},{"style":1691},[14800],{"type":27,"value":13546},{"type":22,"tag":896,"props":14802,"children":14803},{"style":903},[14804],{"type":27,"value":14451},{"type":22,"tag":896,"props":14806,"children":14807},{"style":1691},[14808],{"type":27,"value":2928},{"type":22,"tag":896,"props":14810,"children":14811},{"style":944},[14812],{"type":27,"value":14150},{"type":22,"tag":896,"props":14814,"children":14815},{"style":903},[14816],{"type":27,"value":6377},{"type":22,"tag":896,"props":14818,"children":14819},{"class":898,"line":11592},[14820,14825,14829,14833,14837,14841,14845,14849,14853],{"type":22,"tag":896,"props":14821,"children":14822},{"style":6025},[14823],{"type":27,"value":14824},"                    buf",{"type":22,"tag":896,"props":14826,"children":14827},{"style":903},[14828],{"type":27,"value":14061},{"type":22,"tag":896,"props":14830,"children":14831},{"style":1632},[14832],{"type":27,"value":9362},{"type":22,"tag":896,"props":14834,"children":14835},{"style":903},[14836],{"type":27,"value":7215},{"type":22,"tag":896,"props":14838,"children":14839},{"style":1691},[14840],{"type":27,"value":941},{"type":22,"tag":896,"props":14842,"children":14843},{"style":944},[14844],{"type":27,"value":13677},{"type":22,"tag":896,"props":14846,"children":14847},{"style":1632},[14848],{"type":27,"value":13682},{"type":22,"tag":896,"props":14850,"children":14851},{"style":944},[14852],{"type":27,"value":13687},{"type":22,"tag":896,"props":14854,"children":14855},{"style":903},[14856],{"type":27,"value":1649},{"type":22,"tag":896,"props":14858,"children":14859},{"class":898,"line":11600},[14860,14865,14870,14874],{"type":22,"tag":896,"props":14861,"children":14862},{"style":1691},[14863],{"type":27,"value":14864},"                    *",{"type":22,"tag":896,"props":14866,"children":14867},{"style":903},[14868],{"type":27,"value":14869},"data ",{"type":22,"tag":896,"props":14871,"children":14872},{"style":1691},[14873],{"type":27,"value":941},{"type":22,"tag":896,"props":14875,"children":14876},{"style":903},[14877],{"type":27,"value":14878}," buf;\n",{"type":22,"tag":896,"props":14880,"children":14881},{"class":898,"line":11609},[14882,14887,14891,14895],{"type":22,"tag":896,"props":14883,"children":14884},{"style":1691},[14885],{"type":27,"value":14886},"                    if",{"type":22,"tag":896,"props":14888,"children":14889},{"style":903},[14890],{"type":27,"value":14712},{"type":22,"tag":896,"props":14892,"children":14893},{"style":1691},[14894],{"type":27,"value":906},{"type":22,"tag":896,"props":14896,"children":14897},{"style":903},[14898],{"type":27,"value":14232},{"type":22,"tag":896,"props":14900,"children":14901},{"class":898,"line":11618},[14902,14907,14911],{"type":22,"tag":896,"props":14903,"children":14904},{"style":903},[14905],{"type":27,"value":14906},"                        cp",{"type":22,"tag":896,"props":14908,"children":14909},{"style":1691},[14910],{"type":27,"value":13647},{"type":22,"tag":896,"props":14912,"children":14913},{"style":903},[14914],{"type":27,"value":1649},{"type":22,"tag":896,"props":14916,"children":14917},{"class":898,"line":11636},[14918,14923,14927],{"type":22,"tag":896,"props":14919,"children":14920},{"style":903},[14921],{"type":27,"value":14922},"                    reqpt ",{"type":22,"tag":896,"props":14924,"children":14925},{"style":1691},[14926],{"type":27,"value":941},{"type":22,"tag":896,"props":14928,"children":14929},{"style":903},[14930],{"type":27,"value":14931}," cp;\n",{"type":22,"tag":896,"props":14933,"children":14934},{"class":898,"line":11654},[14935,14940],{"type":22,"tag":896,"props":14936,"children":14937},{"style":1691},[14938],{"type":27,"value":14939},"                    return",{"type":22,"tag":896,"props":14941,"children":14942},{"style":903},[14943],{"type":27,"value":14944}," v;\n",{"type":22,"tag":896,"props":14946,"children":14947},{"class":898,"line":11672},[14948],{"type":22,"tag":896,"props":14949,"children":14950},{"style":903},[14951],{"type":27,"value":14952},"                }\n",{"type":22,"tag":896,"props":14954,"children":14955},{"class":898,"line":11680},[14956],{"type":22,"tag":896,"props":14957,"children":14958},{"emptyLinePlaceholder":3481},[14959],{"type":27,"value":3484},{"type":22,"tag":896,"props":14961,"children":14962},{"class":898,"line":11688},[14963,14967,14971,14975,14979,14983,14987],{"type":22,"tag":896,"props":14964,"children":14965},{"style":1691},[14966],{"type":27,"value":14780},{"type":22,"tag":896,"props":14968,"children":14969},{"style":903},[14970],{"type":27,"value":3643},{"type":22,"tag":896,"props":14972,"children":14973},{"style":1691},[14974],{"type":27,"value":3668},{"type":22,"tag":896,"props":14976,"children":14977},{"style":903},[14978],{"type":27,"value":14451},{"type":22,"tag":896,"props":14980,"children":14981},{"style":1691},[14982],{"type":27,"value":2928},{"type":22,"tag":896,"props":14984,"children":14985},{"style":944},[14986],{"type":27,"value":14539},{"type":22,"tag":896,"props":14988,"children":14989},{"style":903},[14990],{"type":27,"value":6377},{"type":22,"tag":896,"props":14992,"children":14993},{"class":898,"line":11697},[14994,14998,15002],{"type":22,"tag":896,"props":14995,"children":14996},{"style":903},[14997],{"type":27,"value":14757},{"type":22,"tag":896,"props":14999,"children":15000},{"style":1691},[15001],{"type":27,"value":13647},{"type":22,"tag":896,"props":15003,"children":15004},{"style":903},[15005],{"type":27,"value":1649},{"type":22,"tag":896,"props":15007,"children":15008},{"class":898,"line":11705},[15009,15014,15018],{"type":22,"tag":896,"props":15010,"children":15011},{"style":903},[15012],{"type":27,"value":15013},"                    tp ",{"type":22,"tag":896,"props":15015,"children":15016},{"style":1691},[15017],{"type":27,"value":941},{"type":22,"tag":896,"props":15019,"children":15020},{"style":903},[15021],{"type":27,"value":14878},{"type":22,"tag":896,"props":15023,"children":15024},{"class":898,"line":11714},[15025,15030,15034,15038,15042,15046,15050,15054,15058,15062,15066],{"type":22,"tag":896,"props":15026,"children":15027},{"style":1691},[15028],{"type":27,"value":15029},"                    while",{"type":22,"tag":896,"props":15031,"children":15032},{"style":903},[15033],{"type":27,"value":14712},{"type":22,"tag":896,"props":15035,"children":15036},{"style":1691},[15037],{"type":27,"value":906},{"type":22,"tag":896,"props":15039,"children":15040},{"style":903},[15041],{"type":27,"value":14123},{"type":22,"tag":896,"props":15043,"children":15044},{"style":1691},[15045],{"type":27,"value":14128},{"type":22,"tag":896,"props":15047,"children":15048},{"style":933},[15049],{"type":27,"value":14159},{"type":22,"tag":896,"props":15051,"children":15052},{"style":903},[15053],{"type":27,"value":6073},{"type":22,"tag":896,"props":15055,"children":15056},{"style":1691},[15057],{"type":27,"value":3648},{"type":22,"tag":896,"props":15059,"children":15060},{"style":903},[15061],{"type":27,"value":14172},{"type":22,"tag":896,"props":15063,"children":15064},{"style":1691},[15065],{"type":27,"value":3668},{"type":22,"tag":896,"props":15067,"children":15068},{"style":903},[15069],{"type":27,"value":14749},{"type":22,"tag":896,"props":15071,"children":15072},{"class":898,"line":11723},[15073,15077,15081],{"type":22,"tag":896,"props":15074,"children":15075},{"style":903},[15076],{"type":27,"value":14906},{"type":22,"tag":896,"props":15078,"children":15079},{"style":1691},[15080],{"type":27,"value":13647},{"type":22,"tag":896,"props":15082,"children":15083},{"style":903},[15084],{"type":27,"value":1649},{"type":22,"tag":896,"props":15086,"children":15087},{"class":898,"line":11760},[15088],{"type":22,"tag":896,"props":15089,"children":15090},{"emptyLinePlaceholder":3481},[15091],{"type":27,"value":3484},{"type":22,"tag":896,"props":15093,"children":15094},{"class":898,"line":11790},[15095,15099,15103,15107,15111,15115,15119,15123,15127,15131],{"type":22,"tag":896,"props":15096,"children":15097},{"style":1691},[15098],{"type":27,"value":15029},{"type":22,"tag":896,"props":15100,"children":15101},{"style":903},[15102],{"type":27,"value":14712},{"type":22,"tag":896,"props":15104,"children":15105},{"style":1691},[15106],{"type":27,"value":906},{"type":22,"tag":896,"props":15108,"children":15109},{"style":903},[15110],{"type":27,"value":14123},{"type":22,"tag":896,"props":15112,"children":15113},{"style":1691},[15114],{"type":27,"value":14128},{"type":22,"tag":896,"props":15116,"children":15117},{"style":1691},[15118],{"type":27,"value":13546},{"type":22,"tag":896,"props":15120,"children":15121},{"style":903},[15122],{"type":27,"value":14451},{"type":22,"tag":896,"props":15124,"children":15125},{"style":1691},[15126],{"type":27,"value":13672},{"type":22,"tag":896,"props":15128,"children":15129},{"style":944},[15130],{"type":27,"value":14150},{"type":22,"tag":896,"props":15132,"children":15133},{"style":903},[15134],{"type":27,"value":2903},{"type":22,"tag":896,"props":15136,"children":15137},{"class":898,"line":11826},[15138,15143,15147,15151,15155,15159,15163,15167],{"type":22,"tag":896,"props":15139,"children":15140},{"style":1691},[15141],{"type":27,"value":15142},"                        *",{"type":22,"tag":896,"props":15144,"children":15145},{"style":903},[15146],{"type":27,"value":13831},{"type":22,"tag":896,"props":15148,"children":15149},{"style":1691},[15150],{"type":27,"value":13647},{"type":22,"tag":896,"props":15152,"children":15153},{"style":1691},[15154],{"type":27,"value":3638},{"type":22,"tag":896,"props":15156,"children":15157},{"style":1691},[15158],{"type":27,"value":13546},{"type":22,"tag":896,"props":15160,"children":15161},{"style":903},[15162],{"type":27,"value":13820},{"type":22,"tag":896,"props":15164,"children":15165},{"style":1691},[15166],{"type":27,"value":13647},{"type":22,"tag":896,"props":15168,"children":15169},{"style":903},[15170],{"type":27,"value":1649},{"type":22,"tag":896,"props":15172,"children":15173},{"class":898,"line":11861},[15174,15178,15182,15186],{"type":22,"tag":896,"props":15175,"children":15176},{"style":1691},[15177],{"type":27,"value":14886},{"type":22,"tag":896,"props":15179,"children":15180},{"style":903},[15181],{"type":27,"value":14712},{"type":22,"tag":896,"props":15183,"children":15184},{"style":1691},[15185],{"type":27,"value":906},{"type":22,"tag":896,"props":15187,"children":15188},{"style":903},[15189],{"type":27,"value":14232},{"type":22,"tag":896,"props":15191,"children":15192},{"class":898,"line":11895},[15193,15197,15201],{"type":22,"tag":896,"props":15194,"children":15195},{"style":903},[15196],{"type":27,"value":14906},{"type":22,"tag":896,"props":15198,"children":15199},{"style":1691},[15200],{"type":27,"value":13647},{"type":22,"tag":896,"props":15202,"children":15203},{"style":903},[15204],{"type":27,"value":1649},{"type":22,"tag":896,"props":15206,"children":15207},{"class":898,"line":11929},[15208,15212,15216,15220,15224,15228,15232],{"type":22,"tag":896,"props":15209,"children":15210},{"style":1691},[15211],{"type":27,"value":14864},{"type":22,"tag":896,"props":15213,"children":15214},{"style":903},[15215],{"type":27,"value":14502},{"type":22,"tag":896,"props":15217,"children":15218},{"style":1691},[15219],{"type":27,"value":941},{"type":22,"tag":896,"props":15221,"children":15222},{"style":944},[15223],{"type":27,"value":13677},{"type":22,"tag":896,"props":15225,"children":15226},{"style":1632},[15227],{"type":27,"value":13682},{"type":22,"tag":896,"props":15229,"children":15230},{"style":944},[15231],{"type":27,"value":13687},{"type":22,"tag":896,"props":15233,"children":15234},{"style":903},[15235],{"type":27,"value":1649},{"type":22,"tag":896,"props":15237,"children":15238},{"class":898,"line":11937},[15239,15243,15247,15252,15256,15260,15264,15268,15273,15277,15281],{"type":22,"tag":896,"props":15240,"children":15241},{"style":1691},[15242],{"type":27,"value":15029},{"type":22,"tag":896,"props":15244,"children":15245},{"style":903},[15246],{"type":27,"value":3643},{"type":22,"tag":896,"props":15248,"children":15249},{"style":933},[15250],{"type":27,"value":15251},"isspace",{"type":22,"tag":896,"props":15253,"children":15254},{"style":903},[15255],{"type":27,"value":6073},{"type":22,"tag":896,"props":15257,"children":15258},{"style":1691},[15259],{"type":27,"value":3648},{"type":22,"tag":896,"props":15261,"children":15262},{"style":903},[15263],{"type":27,"value":4976},{"type":22,"tag":896,"props":15265,"children":15266},{"style":1691},[15267],{"type":27,"value":3668},{"type":22,"tag":896,"props":15269,"children":15270},{"style":903},[15271],{"type":27,"value":15272},"(tp ",{"type":22,"tag":896,"props":15274,"children":15275},{"style":1691},[15276],{"type":27,"value":4207},{"type":22,"tag":896,"props":15278,"children":15279},{"style":1632},[15280],{"type":27,"value":1688},{"type":22,"tag":896,"props":15282,"children":15283},{"style":903},[15284],{"type":27,"value":15285},"))))\n",{"type":22,"tag":896,"props":15287,"children":15289},{"class":898,"line":15288},64,[15290,15294,15298,15303,15308,15312,15316,15320,15324],{"type":22,"tag":896,"props":15291,"children":15292},{"style":1691},[15293],{"type":27,"value":15142},{"type":22,"tag":896,"props":15295,"children":15296},{"style":903},[15297],{"type":27,"value":2888},{"type":22,"tag":896,"props":15299,"children":15300},{"style":1691},[15301],{"type":27,"value":15302},"--",{"type":22,"tag":896,"props":15304,"children":15305},{"style":903},[15306],{"type":27,"value":15307},"tp) ",{"type":22,"tag":896,"props":15309,"children":15310},{"style":1691},[15311],{"type":27,"value":941},{"type":22,"tag":896,"props":15313,"children":15314},{"style":944},[15315],{"type":27,"value":13677},{"type":22,"tag":896,"props":15317,"children":15318},{"style":1632},[15319],{"type":27,"value":13682},{"type":22,"tag":896,"props":15321,"children":15322},{"style":944},[15323],{"type":27,"value":13687},{"type":22,"tag":896,"props":15325,"children":15326},{"style":903},[15327],{"type":27,"value":1649},{"type":22,"tag":896,"props":15329,"children":15331},{"class":898,"line":15330},65,[15332],{"type":22,"tag":896,"props":15333,"children":15334},{"emptyLinePlaceholder":3481},[15335],{"type":27,"value":3484},{"type":22,"tag":896,"props":15337,"children":15339},{"class":898,"line":15338},66,[15340,15344,15348],{"type":22,"tag":896,"props":15341,"children":15342},{"style":903},[15343],{"type":27,"value":14922},{"type":22,"tag":896,"props":15345,"children":15346},{"style":1691},[15347],{"type":27,"value":941},{"type":22,"tag":896,"props":15349,"children":15350},{"style":903},[15351],{"type":27,"value":14931},{"type":22,"tag":896,"props":15353,"children":15355},{"class":898,"line":15354},67,[15356,15360,15364,15368],{"type":22,"tag":896,"props":15357,"children":15358},{"style":1691},[15359],{"type":27,"value":14864},{"type":22,"tag":896,"props":15361,"children":15362},{"style":903},[15363],{"type":27,"value":14869},{"type":22,"tag":896,"props":15365,"children":15366},{"style":1691},[15367],{"type":27,"value":941},{"type":22,"tag":896,"props":15369,"children":15370},{"style":903},[15371],{"type":27,"value":14878},{"type":22,"tag":896,"props":15373,"children":15375},{"class":898,"line":15374},68,[15376,15380],{"type":22,"tag":896,"props":15377,"children":15378},{"style":1691},[15379],{"type":27,"value":14939},{"type":22,"tag":896,"props":15381,"children":15382},{"style":903},[15383],{"type":27,"value":14944},{"type":22,"tag":896,"props":15385,"children":15387},{"class":898,"line":15386},69,[15388],{"type":22,"tag":896,"props":15389,"children":15390},{"style":903},[15391],{"type":27,"value":14952},{"type":22,"tag":896,"props":15393,"children":15395},{"class":898,"line":15394},70,[15396],{"type":22,"tag":896,"props":15397,"children":15398},{"style":903},[15399],{"type":27,"value":14621},{"type":22,"tag":896,"props":15401,"children":15403},{"class":898,"line":15402},71,[15404,15409,15413],{"type":22,"tag":896,"props":15405,"children":15406},{"style":903},[15407],{"type":27,"value":15408},"            cp ",{"type":22,"tag":896,"props":15410,"children":15411},{"style":1691},[15412],{"type":27,"value":941},{"type":22,"tag":896,"props":15414,"children":15415},{"style":903},[15416],{"type":27,"value":14380},{"type":22,"tag":896,"props":15418,"children":15420},{"class":898,"line":15419},72,[15421],{"type":22,"tag":896,"props":15422,"children":15423},{"style":903},[15424],{"type":27,"value":7177},{"type":22,"tag":896,"props":15426,"children":15428},{"class":898,"line":15427},73,[15429,15434,15438],{"type":22,"tag":896,"props":15430,"children":15431},{"style":903},[15432],{"type":27,"value":15433},"        v",{"type":22,"tag":896,"props":15435,"children":15436},{"style":1691},[15437],{"type":27,"value":13647},{"type":22,"tag":896,"props":15439,"children":15440},{"style":903},[15441],{"type":27,"value":1649},{"type":22,"tag":896,"props":15443,"children":15445},{"class":898,"line":15444},74,[15446],{"type":22,"tag":896,"props":15447,"children":15448},{"style":903},[15449],{"type":27,"value":7506},{"type":22,"tag":896,"props":15451,"children":15453},{"class":898,"line":15452},75,[15454,15458],{"type":22,"tag":896,"props":15455,"children":15456},{"style":1691},[15457],{"type":27,"value":3493},{"type":22,"tag":896,"props":15459,"children":15460},{"style":903},[15461],{"type":27,"value":14944},{"type":22,"tag":896,"props":15463,"children":15465},{"class":898,"line":15464},76,[15466],{"type":22,"tag":896,"props":15467,"children":15468},{"style":903},[15469],{"type":27,"value":1745},{"type":22,"tag":23,"props":15471,"children":15472},{},[15473],{"type":27,"value":15474},"Feel free to pause here and try to identify a problem in this function unassisted, otherwise continue on and we'll break it down in detail.",{"type":22,"tag":193,"props":15476,"children":15478},{"id":15477},"spotting-the-vulnerability",[15479],{"type":27,"value":15480},"Spotting the Vulnerability",{"type":22,"tag":23,"props":15482,"children":15483},{},[15484,15486,15491,15493,15498,15500,15505],{"type":27,"value":15485},"Taking the perspective of an attacker is an effective way of switching from the typical development mindset of \"should this work correctly?\" to the mindset of \"can this work ",{"type":22,"tag":267,"props":15487,"children":15488},{},[15489],{"type":27,"value":15490},"incorrectly?",{"type":27,"value":15492},"\" As an attacker who is ",{"type":22,"tag":267,"props":15494,"children":15495},{},[15496],{"type":27,"value":15497},"trying",{"type":27,"value":15499}," to overrun ",{"type":22,"tag":486,"props":15501,"children":15503},{"className":15502},[],[15504],{"type":27,"value":13809},{"type":27,"value":15506},", your goals would be to",{"type":22,"tag":847,"props":15508,"children":15509},{},[15510,15529],{"type":22,"tag":115,"props":15511,"children":15512},{},[15513,15515,15520,15522,15527],{"type":27,"value":15514},"reach the code where ",{"type":22,"tag":486,"props":15516,"children":15518},{"className":15517},[],[15519],{"type":27,"value":13809},{"type":27,"value":15521}," gets written to via the ",{"type":22,"tag":486,"props":15523,"children":15525},{"className":15524},[],[15526],{"type":27,"value":13831},{"type":27,"value":15528}," pointer, and",{"type":22,"tag":115,"props":15530,"children":15531},{},[15532,15534,15539],{"type":27,"value":15533},"find a means to increment ",{"type":22,"tag":486,"props":15535,"children":15537},{"className":15536},[],[15538],{"type":27,"value":13831},{"type":27,"value":15540}," 128 or more times.",{"type":22,"tag":23,"props":15542,"children":15543},{},[15544],{"type":27,"value":15545},"Starting with this first goal, we first have",{"type":22,"tag":886,"props":15547,"children":15549},{"className":13527,"code":15548,"language":13529,"meta":8,"style":8},"tp = v->text;\nwhile (*tp != '\\0' && *tp != '=' && cp \u003C reqend && *cp == *tp) {\n    cp++;\n    tp++;\n}\n\nif ((*tp == '\\0') || (*tp == '=')) {\n    while (cp \u003C reqend && isspace((int)*cp))\n        cp++;\n",[15550],{"type":22,"tag":486,"props":15551,"children":15552},{"__ignoreMap":8},[15553,15579,15675,15691,15707,15714,15721,15788,15835],{"type":22,"tag":896,"props":15554,"children":15555},{"class":898,"line":899},[15556,15560,15564,15569,15574],{"type":22,"tag":896,"props":15557,"children":15558},{"style":903},[15559],{"type":27,"value":14502},{"type":22,"tag":896,"props":15561,"children":15562},{"style":1691},[15563],{"type":27,"value":941},{"type":22,"tag":896,"props":15565,"children":15566},{"style":903},[15567],{"type":27,"value":15568}," v",{"type":22,"tag":896,"props":15570,"children":15571},{"style":1691},[15572],{"type":27,"value":15573},"->",{"type":22,"tag":896,"props":15575,"children":15576},{"style":903},[15577],{"type":27,"value":15578},"text;\n",{"type":22,"tag":896,"props":15580,"children":15581},{"class":898,"line":758},[15582,15587,15591,15595,15599,15603,15607,15611,15615,15619,15623,15627,15631,15635,15639,15643,15647,15651,15655,15659,15663,15667,15671],{"type":22,"tag":896,"props":15583,"children":15584},{"style":1691},[15585],{"type":27,"value":15586},"while",{"type":22,"tag":896,"props":15588,"children":15589},{"style":903},[15590],{"type":27,"value":3643},{"type":22,"tag":896,"props":15592,"children":15593},{"style":1691},[15594],{"type":27,"value":3668},{"type":22,"tag":896,"props":15596,"children":15597},{"style":903},[15598],{"type":27,"value":14502},{"type":22,"tag":896,"props":15600,"children":15601},{"style":1691},[15602],{"type":27,"value":13672},{"type":22,"tag":896,"props":15604,"children":15605},{"style":944},[15606],{"type":27,"value":13677},{"type":22,"tag":896,"props":15608,"children":15609},{"style":1632},[15610],{"type":27,"value":13682},{"type":22,"tag":896,"props":15612,"children":15613},{"style":944},[15614],{"type":27,"value":13687},{"type":22,"tag":896,"props":15616,"children":15617},{"style":1691},[15618],{"type":27,"value":2980},{"type":22,"tag":896,"props":15620,"children":15621},{"style":1691},[15622],{"type":27,"value":13546},{"type":22,"tag":896,"props":15624,"children":15625},{"style":903},[15626],{"type":27,"value":14502},{"type":22,"tag":896,"props":15628,"children":15629},{"style":1691},[15630],{"type":27,"value":13672},{"type":22,"tag":896,"props":15632,"children":15633},{"style":944},[15634],{"type":27,"value":14539},{"type":22,"tag":896,"props":15636,"children":15637},{"style":1691},[15638],{"type":27,"value":2980},{"type":22,"tag":896,"props":15640,"children":15641},{"style":903},[15642],{"type":27,"value":14548},{"type":22,"tag":896,"props":15644,"children":15645},{"style":1691},[15646],{"type":27,"value":906},{"type":22,"tag":896,"props":15648,"children":15649},{"style":903},[15650],{"type":27,"value":14123},{"type":22,"tag":896,"props":15652,"children":15653},{"style":1691},[15654],{"type":27,"value":14128},{"type":22,"tag":896,"props":15656,"children":15657},{"style":1691},[15658],{"type":27,"value":13546},{"type":22,"tag":896,"props":15660,"children":15661},{"style":903},[15662],{"type":27,"value":14451},{"type":22,"tag":896,"props":15664,"children":15665},{"style":1691},[15666],{"type":27,"value":2928},{"type":22,"tag":896,"props":15668,"children":15669},{"style":1691},[15670],{"type":27,"value":13546},{"type":22,"tag":896,"props":15672,"children":15673},{"style":903},[15674],{"type":27,"value":14581},{"type":22,"tag":896,"props":15676,"children":15677},{"class":898,"line":755},[15678,15683,15687],{"type":22,"tag":896,"props":15679,"children":15680},{"style":903},[15681],{"type":27,"value":15682},"    cp",{"type":22,"tag":896,"props":15684,"children":15685},{"style":1691},[15686],{"type":27,"value":13647},{"type":22,"tag":896,"props":15688,"children":15689},{"style":903},[15690],{"type":27,"value":1649},{"type":22,"tag":896,"props":15692,"children":15693},{"class":898,"line":983},[15694,15699,15703],{"type":22,"tag":896,"props":15695,"children":15696},{"style":903},[15697],{"type":27,"value":15698},"    tp",{"type":22,"tag":896,"props":15700,"children":15701},{"style":1691},[15702],{"type":27,"value":13647},{"type":22,"tag":896,"props":15704,"children":15705},{"style":903},[15706],{"type":27,"value":1649},{"type":22,"tag":896,"props":15708,"children":15709},{"class":898,"line":1013},[15710],{"type":22,"tag":896,"props":15711,"children":15712},{"style":903},[15713],{"type":27,"value":1745},{"type":22,"tag":896,"props":15715,"children":15716},{"class":898,"line":1030},[15717],{"type":22,"tag":896,"props":15718,"children":15719},{"emptyLinePlaceholder":3481},[15720],{"type":27,"value":3484},{"type":22,"tag":896,"props":15722,"children":15723},{"class":898,"line":1068},[15724,15728,15732,15736,15740,15744,15748,15752,15756,15760,15764,15768,15772,15776,15780,15784],{"type":22,"tag":896,"props":15725,"children":15726},{"style":1691},[15727],{"type":27,"value":2519},{"type":22,"tag":896,"props":15729,"children":15730},{"style":903},[15731],{"type":27,"value":14641},{"type":22,"tag":896,"props":15733,"children":15734},{"style":1691},[15735],{"type":27,"value":3668},{"type":22,"tag":896,"props":15737,"children":15738},{"style":903},[15739],{"type":27,"value":14502},{"type":22,"tag":896,"props":15741,"children":15742},{"style":1691},[15743],{"type":27,"value":2928},{"type":22,"tag":896,"props":15745,"children":15746},{"style":944},[15747],{"type":27,"value":13677},{"type":22,"tag":896,"props":15749,"children":15750},{"style":1632},[15751],{"type":27,"value":13682},{"type":22,"tag":896,"props":15753,"children":15754},{"style":944},[15755],{"type":27,"value":13687},{"type":22,"tag":896,"props":15757,"children":15758},{"style":903},[15759],{"type":27,"value":6082},{"type":22,"tag":896,"props":15761,"children":15762},{"style":1691},[15763],{"type":27,"value":14674},{"type":22,"tag":896,"props":15765,"children":15766},{"style":903},[15767],{"type":27,"value":3643},{"type":22,"tag":896,"props":15769,"children":15770},{"style":1691},[15771],{"type":27,"value":3668},{"type":22,"tag":896,"props":15773,"children":15774},{"style":903},[15775],{"type":27,"value":14502},{"type":22,"tag":896,"props":15777,"children":15778},{"style":1691},[15779],{"type":27,"value":2928},{"type":22,"tag":896,"props":15781,"children":15782},{"style":944},[15783],{"type":27,"value":14539},{"type":22,"tag":896,"props":15785,"children":15786},{"style":903},[15787],{"type":27,"value":14699},{"type":22,"tag":896,"props":15789,"children":15790},{"class":898,"line":1085},[15791,15795,15799,15803,15807,15811,15815,15819,15823,15827,15831],{"type":22,"tag":896,"props":15792,"children":15793},{"style":1691},[15794],{"type":27,"value":13630},{"type":22,"tag":896,"props":15796,"children":15797},{"style":903},[15798],{"type":27,"value":14712},{"type":22,"tag":896,"props":15800,"children":15801},{"style":1691},[15802],{"type":27,"value":906},{"type":22,"tag":896,"props":15804,"children":15805},{"style":903},[15806],{"type":27,"value":14123},{"type":22,"tag":896,"props":15808,"children":15809},{"style":1691},[15810],{"type":27,"value":14128},{"type":22,"tag":896,"props":15812,"children":15813},{"style":933},[15814],{"type":27,"value":14159},{"type":22,"tag":896,"props":15816,"children":15817},{"style":903},[15818],{"type":27,"value":6073},{"type":22,"tag":896,"props":15820,"children":15821},{"style":1691},[15822],{"type":27,"value":3648},{"type":22,"tag":896,"props":15824,"children":15825},{"style":903},[15826],{"type":27,"value":14172},{"type":22,"tag":896,"props":15828,"children":15829},{"style":1691},[15830],{"type":27,"value":3668},{"type":22,"tag":896,"props":15832,"children":15833},{"style":903},[15834],{"type":27,"value":14749},{"type":22,"tag":896,"props":15836,"children":15837},{"class":898,"line":1123},[15838,15843,15847],{"type":22,"tag":896,"props":15839,"children":15840},{"style":903},[15841],{"type":27,"value":15842},"        cp",{"type":22,"tag":896,"props":15844,"children":15845},{"style":1691},[15846],{"type":27,"value":13647},{"type":22,"tag":896,"props":15848,"children":15849},{"style":903},[15850],{"type":27,"value":1649},{"type":22,"tag":23,"props":15852,"children":15853},{},[15854,15856,15861,15863,15868,15870,15875,15876,15881],{"type":27,"value":15855},"While looping through variables definitions in order to look for one which matches the input data, we're initially using ",{"type":22,"tag":486,"props":15857,"children":15859},{"className":15858},[],[15860],{"type":27,"value":13831},{"type":27,"value":15862}," as a pointer into the variable's ",{"type":22,"tag":486,"props":15864,"children":15866},{"className":15865},[],[15867],{"type":27,"value":27},{"type":27,"value":15869}," field. If the input matches that text up until a ",{"type":22,"tag":486,"props":15871,"children":15873},{"className":15872},[],[15874],{"type":27,"value":13742},{"type":27,"value":2684},{"type":22,"tag":486,"props":15877,"children":15879},{"className":15878},[],[15880],{"type":27,"value":941},{"type":27,"value":15882}," character, this block gets executed. Next,",{"type":22,"tag":886,"props":15884,"children":15886},{"className":13527,"code":15885,"language":13529,"meta":8,"style":8},"    if (cp == reqend || *cp == ',') {\n        buf[0] = '\\0';\n        *data = buf;\n        if (cp \u003C reqend)\n            cp++;\n        reqpt = cp;\n        return v;\n    }\n",[15887],{"type":22,"tag":486,"props":15888,"children":15889},{"__ignoreMap":8},[15890,15933,15973,15993,16012,16028,16044,16055],{"type":22,"tag":896,"props":15891,"children":15892},{"class":898,"line":899},[15893,15897,15901,15905,15909,15913,15917,15921,15925,15929],{"type":22,"tag":896,"props":15894,"children":15895},{"style":1691},[15896],{"type":27,"value":2918},{"type":22,"tag":896,"props":15898,"children":15899},{"style":903},[15900],{"type":27,"value":14712},{"type":22,"tag":896,"props":15902,"children":15903},{"style":1691},[15904],{"type":27,"value":2928},{"type":22,"tag":896,"props":15906,"children":15907},{"style":903},[15908],{"type":27,"value":14123},{"type":22,"tag":896,"props":15910,"children":15911},{"style":1691},[15912],{"type":27,"value":14674},{"type":22,"tag":896,"props":15914,"children":15915},{"style":1691},[15916],{"type":27,"value":13546},{"type":22,"tag":896,"props":15918,"children":15919},{"style":903},[15920],{"type":27,"value":14451},{"type":22,"tag":896,"props":15922,"children":15923},{"style":1691},[15924],{"type":27,"value":2928},{"type":22,"tag":896,"props":15926,"children":15927},{"style":944},[15928],{"type":27,"value":14150},{"type":22,"tag":896,"props":15930,"children":15931},{"style":903},[15932],{"type":27,"value":6377},{"type":22,"tag":896,"props":15934,"children":15935},{"class":898,"line":758},[15936,15941,15945,15949,15953,15957,15961,15965,15969],{"type":22,"tag":896,"props":15937,"children":15938},{"style":6025},[15939],{"type":27,"value":15940},"        buf",{"type":22,"tag":896,"props":15942,"children":15943},{"style":903},[15944],{"type":27,"value":14061},{"type":22,"tag":896,"props":15946,"children":15947},{"style":1632},[15948],{"type":27,"value":9362},{"type":22,"tag":896,"props":15950,"children":15951},{"style":903},[15952],{"type":27,"value":7215},{"type":22,"tag":896,"props":15954,"children":15955},{"style":1691},[15956],{"type":27,"value":941},{"type":22,"tag":896,"props":15958,"children":15959},{"style":944},[15960],{"type":27,"value":13677},{"type":22,"tag":896,"props":15962,"children":15963},{"style":1632},[15964],{"type":27,"value":13682},{"type":22,"tag":896,"props":15966,"children":15967},{"style":944},[15968],{"type":27,"value":13687},{"type":22,"tag":896,"props":15970,"children":15971},{"style":903},[15972],{"type":27,"value":1649},{"type":22,"tag":896,"props":15974,"children":15975},{"class":898,"line":755},[15976,15981,15985,15989],{"type":22,"tag":896,"props":15977,"children":15978},{"style":1691},[15979],{"type":27,"value":15980},"        *",{"type":22,"tag":896,"props":15982,"children":15983},{"style":903},[15984],{"type":27,"value":14869},{"type":22,"tag":896,"props":15986,"children":15987},{"style":1691},[15988],{"type":27,"value":941},{"type":22,"tag":896,"props":15990,"children":15991},{"style":903},[15992],{"type":27,"value":14878},{"type":22,"tag":896,"props":15994,"children":15995},{"class":898,"line":983},[15996,16000,16004,16008],{"type":22,"tag":896,"props":15997,"children":15998},{"style":1691},[15999],{"type":27,"value":7124},{"type":22,"tag":896,"props":16001,"children":16002},{"style":903},[16003],{"type":27,"value":14712},{"type":22,"tag":896,"props":16005,"children":16006},{"style":1691},[16007],{"type":27,"value":906},{"type":22,"tag":896,"props":16009,"children":16010},{"style":903},[16011],{"type":27,"value":14232},{"type":22,"tag":896,"props":16013,"children":16014},{"class":898,"line":1013},[16015,16020,16024],{"type":22,"tag":896,"props":16016,"children":16017},{"style":903},[16018],{"type":27,"value":16019},"            cp",{"type":22,"tag":896,"props":16021,"children":16022},{"style":1691},[16023],{"type":27,"value":13647},{"type":22,"tag":896,"props":16025,"children":16026},{"style":903},[16027],{"type":27,"value":1649},{"type":22,"tag":896,"props":16029,"children":16030},{"class":898,"line":1030},[16031,16036,16040],{"type":22,"tag":896,"props":16032,"children":16033},{"style":903},[16034],{"type":27,"value":16035},"        reqpt ",{"type":22,"tag":896,"props":16037,"children":16038},{"style":1691},[16039],{"type":27,"value":941},{"type":22,"tag":896,"props":16041,"children":16042},{"style":903},[16043],{"type":27,"value":14931},{"type":22,"tag":896,"props":16045,"children":16046},{"class":898,"line":1068},[16047,16051],{"type":22,"tag":896,"props":16048,"children":16049},{"style":1691},[16050],{"type":27,"value":2945},{"type":22,"tag":896,"props":16052,"children":16053},{"style":903},[16054],{"type":27,"value":14944},{"type":22,"tag":896,"props":16056,"children":16057},{"class":898,"line":1085},[16058],{"type":22,"tag":896,"props":16059,"children":16060},{"style":903},[16061],{"type":27,"value":7506},{"type":22,"tag":23,"props":16063,"children":16064},{},[16065],{"type":27,"value":16066},"If the input data contains a comma, this block will execute and return from the function. So to continue on, the input must not contain a comma.",{"type":22,"tag":886,"props":16068,"children":16070},{"className":13527,"code":16069,"language":13529,"meta":8,"style":8},"    if (*cp == '=') {\n        cp++;\n        tp = buf;\n        while (cp \u003C reqend && isspace((int)*cp))\n            cp++;\n",[16071],{"type":22,"tag":486,"props":16072,"children":16073},{"__ignoreMap":8},[16074,16105,16120,16136,16184],{"type":22,"tag":896,"props":16075,"children":16076},{"class":898,"line":899},[16077,16081,16085,16089,16093,16097,16101],{"type":22,"tag":896,"props":16078,"children":16079},{"style":1691},[16080],{"type":27,"value":2918},{"type":22,"tag":896,"props":16082,"children":16083},{"style":903},[16084],{"type":27,"value":3643},{"type":22,"tag":896,"props":16086,"children":16087},{"style":1691},[16088],{"type":27,"value":3668},{"type":22,"tag":896,"props":16090,"children":16091},{"style":903},[16092],{"type":27,"value":14451},{"type":22,"tag":896,"props":16094,"children":16095},{"style":1691},[16096],{"type":27,"value":2928},{"type":22,"tag":896,"props":16098,"children":16099},{"style":944},[16100],{"type":27,"value":14539},{"type":22,"tag":896,"props":16102,"children":16103},{"style":903},[16104],{"type":27,"value":6377},{"type":22,"tag":896,"props":16106,"children":16107},{"class":898,"line":758},[16108,16112,16116],{"type":22,"tag":896,"props":16109,"children":16110},{"style":903},[16111],{"type":27,"value":15842},{"type":22,"tag":896,"props":16113,"children":16114},{"style":1691},[16115],{"type":27,"value":13647},{"type":22,"tag":896,"props":16117,"children":16118},{"style":903},[16119],{"type":27,"value":1649},{"type":22,"tag":896,"props":16121,"children":16122},{"class":898,"line":755},[16123,16128,16132],{"type":22,"tag":896,"props":16124,"children":16125},{"style":903},[16126],{"type":27,"value":16127},"        tp ",{"type":22,"tag":896,"props":16129,"children":16130},{"style":1691},[16131],{"type":27,"value":941},{"type":22,"tag":896,"props":16133,"children":16134},{"style":903},[16135],{"type":27,"value":14878},{"type":22,"tag":896,"props":16137,"children":16138},{"class":898,"line":983},[16139,16144,16148,16152,16156,16160,16164,16168,16172,16176,16180],{"type":22,"tag":896,"props":16140,"children":16141},{"style":1691},[16142],{"type":27,"value":16143},"        while",{"type":22,"tag":896,"props":16145,"children":16146},{"style":903},[16147],{"type":27,"value":14712},{"type":22,"tag":896,"props":16149,"children":16150},{"style":1691},[16151],{"type":27,"value":906},{"type":22,"tag":896,"props":16153,"children":16154},{"style":903},[16155],{"type":27,"value":14123},{"type":22,"tag":896,"props":16157,"children":16158},{"style":1691},[16159],{"type":27,"value":14128},{"type":22,"tag":896,"props":16161,"children":16162},{"style":933},[16163],{"type":27,"value":14159},{"type":22,"tag":896,"props":16165,"children":16166},{"style":903},[16167],{"type":27,"value":6073},{"type":22,"tag":896,"props":16169,"children":16170},{"style":1691},[16171],{"type":27,"value":3648},{"type":22,"tag":896,"props":16173,"children":16174},{"style":903},[16175],{"type":27,"value":14172},{"type":22,"tag":896,"props":16177,"children":16178},{"style":1691},[16179],{"type":27,"value":3668},{"type":22,"tag":896,"props":16181,"children":16182},{"style":903},[16183],{"type":27,"value":14749},{"type":22,"tag":896,"props":16185,"children":16186},{"class":898,"line":1013},[16187,16191,16195],{"type":22,"tag":896,"props":16188,"children":16189},{"style":903},[16190],{"type":27,"value":16019},{"type":22,"tag":896,"props":16192,"children":16193},{"style":1691},[16194],{"type":27,"value":13647},{"type":22,"tag":896,"props":16196,"children":16197},{"style":903},[16198],{"type":27,"value":1649},{"type":22,"tag":23,"props":16200,"children":16201},{},[16202],{"type":27,"value":16203},"So long as the input data contains an equals sign, this block will execute. Finally,",{"type":22,"tag":886,"props":16205,"children":16207},{"className":13527,"code":16206,"language":13529,"meta":8,"style":8},"        while (cp \u003C reqend && *cp != ',')\n            *tp++ = *cp++;\n        if (cp \u003C reqend)\n            cp++;\n        *tp = '\\0';\n        while (isspace((int)(*(tp - 1))))\n            *(--tp) = '\\0';\n",[16208],{"type":22,"tag":486,"props":16209,"children":16210},{"__ignoreMap":8},[16211,16254,16290,16309,16324,16355,16402],{"type":22,"tag":896,"props":16212,"children":16213},{"class":898,"line":899},[16214,16218,16222,16226,16230,16234,16238,16242,16246,16250],{"type":22,"tag":896,"props":16215,"children":16216},{"style":1691},[16217],{"type":27,"value":16143},{"type":22,"tag":896,"props":16219,"children":16220},{"style":903},[16221],{"type":27,"value":14712},{"type":22,"tag":896,"props":16223,"children":16224},{"style":1691},[16225],{"type":27,"value":906},{"type":22,"tag":896,"props":16227,"children":16228},{"style":903},[16229],{"type":27,"value":14123},{"type":22,"tag":896,"props":16231,"children":16232},{"style":1691},[16233],{"type":27,"value":14128},{"type":22,"tag":896,"props":16235,"children":16236},{"style":1691},[16237],{"type":27,"value":13546},{"type":22,"tag":896,"props":16239,"children":16240},{"style":903},[16241],{"type":27,"value":14451},{"type":22,"tag":896,"props":16243,"children":16244},{"style":1691},[16245],{"type":27,"value":13672},{"type":22,"tag":896,"props":16247,"children":16248},{"style":944},[16249],{"type":27,"value":14150},{"type":22,"tag":896,"props":16251,"children":16252},{"style":903},[16253],{"type":27,"value":2903},{"type":22,"tag":896,"props":16255,"children":16256},{"class":898,"line":758},[16257,16262,16266,16270,16274,16278,16282,16286],{"type":22,"tag":896,"props":16258,"children":16259},{"style":1691},[16260],{"type":27,"value":16261},"            *",{"type":22,"tag":896,"props":16263,"children":16264},{"style":903},[16265],{"type":27,"value":13831},{"type":22,"tag":896,"props":16267,"children":16268},{"style":1691},[16269],{"type":27,"value":13647},{"type":22,"tag":896,"props":16271,"children":16272},{"style":1691},[16273],{"type":27,"value":3638},{"type":22,"tag":896,"props":16275,"children":16276},{"style":1691},[16277],{"type":27,"value":13546},{"type":22,"tag":896,"props":16279,"children":16280},{"style":903},[16281],{"type":27,"value":13820},{"type":22,"tag":896,"props":16283,"children":16284},{"style":1691},[16285],{"type":27,"value":13647},{"type":22,"tag":896,"props":16287,"children":16288},{"style":903},[16289],{"type":27,"value":1649},{"type":22,"tag":896,"props":16291,"children":16292},{"class":898,"line":755},[16293,16297,16301,16305],{"type":22,"tag":896,"props":16294,"children":16295},{"style":1691},[16296],{"type":27,"value":7124},{"type":22,"tag":896,"props":16298,"children":16299},{"style":903},[16300],{"type":27,"value":14712},{"type":22,"tag":896,"props":16302,"children":16303},{"style":1691},[16304],{"type":27,"value":906},{"type":22,"tag":896,"props":16306,"children":16307},{"style":903},[16308],{"type":27,"value":14232},{"type":22,"tag":896,"props":16310,"children":16311},{"class":898,"line":983},[16312,16316,16320],{"type":22,"tag":896,"props":16313,"children":16314},{"style":903},[16315],{"type":27,"value":16019},{"type":22,"tag":896,"props":16317,"children":16318},{"style":1691},[16319],{"type":27,"value":13647},{"type":22,"tag":896,"props":16321,"children":16322},{"style":903},[16323],{"type":27,"value":1649},{"type":22,"tag":896,"props":16325,"children":16326},{"class":898,"line":1013},[16327,16331,16335,16339,16343,16347,16351],{"type":22,"tag":896,"props":16328,"children":16329},{"style":1691},[16330],{"type":27,"value":15980},{"type":22,"tag":896,"props":16332,"children":16333},{"style":903},[16334],{"type":27,"value":14502},{"type":22,"tag":896,"props":16336,"children":16337},{"style":1691},[16338],{"type":27,"value":941},{"type":22,"tag":896,"props":16340,"children":16341},{"style":944},[16342],{"type":27,"value":13677},{"type":22,"tag":896,"props":16344,"children":16345},{"style":1632},[16346],{"type":27,"value":13682},{"type":22,"tag":896,"props":16348,"children":16349},{"style":944},[16350],{"type":27,"value":13687},{"type":22,"tag":896,"props":16352,"children":16353},{"style":903},[16354],{"type":27,"value":1649},{"type":22,"tag":896,"props":16356,"children":16357},{"class":898,"line":1030},[16358,16362,16366,16370,16374,16378,16382,16386,16390,16394,16398],{"type":22,"tag":896,"props":16359,"children":16360},{"style":1691},[16361],{"type":27,"value":16143},{"type":22,"tag":896,"props":16363,"children":16364},{"style":903},[16365],{"type":27,"value":3643},{"type":22,"tag":896,"props":16367,"children":16368},{"style":933},[16369],{"type":27,"value":15251},{"type":22,"tag":896,"props":16371,"children":16372},{"style":903},[16373],{"type":27,"value":6073},{"type":22,"tag":896,"props":16375,"children":16376},{"style":1691},[16377],{"type":27,"value":3648},{"type":22,"tag":896,"props":16379,"children":16380},{"style":903},[16381],{"type":27,"value":4976},{"type":22,"tag":896,"props":16383,"children":16384},{"style":1691},[16385],{"type":27,"value":3668},{"type":22,"tag":896,"props":16387,"children":16388},{"style":903},[16389],{"type":27,"value":15272},{"type":22,"tag":896,"props":16391,"children":16392},{"style":1691},[16393],{"type":27,"value":4207},{"type":22,"tag":896,"props":16395,"children":16396},{"style":1632},[16397],{"type":27,"value":1688},{"type":22,"tag":896,"props":16399,"children":16400},{"style":903},[16401],{"type":27,"value":15285},{"type":22,"tag":896,"props":16403,"children":16404},{"class":898,"line":1068},[16405,16409,16413,16417,16421,16425,16429,16433,16437],{"type":22,"tag":896,"props":16406,"children":16407},{"style":1691},[16408],{"type":27,"value":16261},{"type":22,"tag":896,"props":16410,"children":16411},{"style":903},[16412],{"type":27,"value":2888},{"type":22,"tag":896,"props":16414,"children":16415},{"style":1691},[16416],{"type":27,"value":15302},{"type":22,"tag":896,"props":16418,"children":16419},{"style":903},[16420],{"type":27,"value":15307},{"type":22,"tag":896,"props":16422,"children":16423},{"style":1691},[16424],{"type":27,"value":941},{"type":22,"tag":896,"props":16426,"children":16427},{"style":944},[16428],{"type":27,"value":13677},{"type":22,"tag":896,"props":16430,"children":16431},{"style":1632},[16432],{"type":27,"value":13682},{"type":22,"tag":896,"props":16434,"children":16435},{"style":944},[16436],{"type":27,"value":13687},{"type":22,"tag":896,"props":16438,"children":16439},{"style":903},[16440],{"type":27,"value":1649},{"type":22,"tag":23,"props":16442,"children":16443},{},[16444,16446,16451,16453,16458,16460,16465,16467,16473,16475,16480],{"type":27,"value":16445},"We've reached the code which copies into ",{"type":22,"tag":486,"props":16447,"children":16449},{"className":16448},[],[16450],{"type":27,"value":13809},{"type":27,"value":16452}," (via ",{"type":22,"tag":486,"props":16454,"children":16456},{"className":16455},[],[16457],{"type":27,"value":13831},{"type":27,"value":16459},"). So long as ",{"type":22,"tag":486,"props":16461,"children":16463},{"className":16462},[],[16464],{"type":27,"value":13820},{"type":27,"value":16466}," is not a comma and is less than the end of the input data (",{"type":22,"tag":486,"props":16468,"children":16470},{"className":16469},[],[16471],{"type":27,"value":16472},"reqend",{"type":27,"value":16474},"), the copy continues. Notably, no checks prevent this loop from executing 128+ times and blowing the stack of ",{"type":22,"tag":486,"props":16476,"children":16478},{"className":16477},[],[16479],{"type":27,"value":13809},{"type":27,"value":16481},", since the input data can exceed that size. Reviewing the preconditions we've built up for reaching this point in the code, we only have",{"type":22,"tag":847,"props":16483,"children":16484},{},[16485,16490],{"type":22,"tag":115,"props":16486,"children":16487},{},[16488],{"type":27,"value":16489},"input does not contain a comma, and",{"type":22,"tag":115,"props":16491,"children":16492},{},[16493],{"type":27,"value":16494},"input contains an equals sign.",{"type":22,"tag":23,"props":16496,"children":16497},{},[16498,16500,16505],{"type":27,"value":16499},"This sounds loose enough to be practically exploitable, and it turns out it was, by writing and executing malicious code on the stack beyond ",{"type":22,"tag":486,"props":16501,"children":16503},{"className":16502},[],[16504],{"type":27,"value":13809},{"type":27,"value":16506},". The lack of bounds checking is obvious once it's singled out, but buried among the other checks and validations, it's much harder to find.",{"type":22,"tag":193,"props":16508,"children":16510},{"id":16509},"mitigation-in-ntpd",[16511],{"type":27,"value":16512},"Mitigation in NTPD",{"type":22,"tag":23,"props":16514,"children":16515},{},[16516,16518,16523],{"type":27,"value":16517},"The mitigation applied for this vulnerability was as simple as you might expect: adding another condition to the ",{"type":22,"tag":486,"props":16519,"children":16521},{"className":16520},[],[16522],{"type":27,"value":15586},{"type":27,"value":16524}," loop performing the copy:",{"type":22,"tag":886,"props":16526,"children":16528},{"className":13527,"code":16527,"language":13529,"meta":8,"style":8},"        while (cp \u003C reqend && *cp != ',') {\n            *tp++ = *cp++;\n            if (tp >= buf + sizeof(buf) - 1) {\n#if 0  /* don't syslog for now - DoS potential on filling syslog */\n                msyslog(LOG_WARNING,\n                    \"Attempted \\\"ntpdx\\\" exploit from IP %d.%d.%d.%d:%d (possibly spoofed)\\n\",\n                    (ntohl(rmt_addr->sin_addr.s_addr) >> 24) & 0xff,\n                    (ntohl(rmt_addr->sin_addr.s_addr) >> 16) & 0xff,\n                    (ntohl(rmt_addr->sin_addr.s_addr) >> 8) & 0xff,\n                    (ntohl(rmt_addr->sin_addr.s_addr) >> 0) & 0xff,\n                    ntohs(rmt_addr->sin_port));\n#endif\n                return (0);\n            }\n        }\n",[16529],{"type":22,"tag":486,"props":16530,"children":16531},{"__ignoreMap":8},[16532,16575,16610,16657,16674,16682,16690,16698,16706,16714,16722,16730,16738,16758,16765],{"type":22,"tag":896,"props":16533,"children":16534},{"class":898,"line":899},[16535,16539,16543,16547,16551,16555,16559,16563,16567,16571],{"type":22,"tag":896,"props":16536,"children":16537},{"style":1691},[16538],{"type":27,"value":16143},{"type":22,"tag":896,"props":16540,"children":16541},{"style":903},[16542],{"type":27,"value":14712},{"type":22,"tag":896,"props":16544,"children":16545},{"style":1691},[16546],{"type":27,"value":906},{"type":22,"tag":896,"props":16548,"children":16549},{"style":903},[16550],{"type":27,"value":14123},{"type":22,"tag":896,"props":16552,"children":16553},{"style":1691},[16554],{"type":27,"value":14128},{"type":22,"tag":896,"props":16556,"children":16557},{"style":1691},[16558],{"type":27,"value":13546},{"type":22,"tag":896,"props":16560,"children":16561},{"style":903},[16562],{"type":27,"value":14451},{"type":22,"tag":896,"props":16564,"children":16565},{"style":1691},[16566],{"type":27,"value":13672},{"type":22,"tag":896,"props":16568,"children":16569},{"style":944},[16570],{"type":27,"value":14150},{"type":22,"tag":896,"props":16572,"children":16573},{"style":903},[16574],{"type":27,"value":6377},{"type":22,"tag":896,"props":16576,"children":16577},{"class":898,"line":758},[16578,16582,16586,16590,16594,16598,16602,16606],{"type":22,"tag":896,"props":16579,"children":16580},{"style":1691},[16581],{"type":27,"value":16261},{"type":22,"tag":896,"props":16583,"children":16584},{"style":903},[16585],{"type":27,"value":13831},{"type":22,"tag":896,"props":16587,"children":16588},{"style":1691},[16589],{"type":27,"value":13647},{"type":22,"tag":896,"props":16591,"children":16592},{"style":1691},[16593],{"type":27,"value":3638},{"type":22,"tag":896,"props":16595,"children":16596},{"style":1691},[16597],{"type":27,"value":13546},{"type":22,"tag":896,"props":16599,"children":16600},{"style":903},[16601],{"type":27,"value":13820},{"type":22,"tag":896,"props":16603,"children":16604},{"style":1691},[16605],{"type":27,"value":13647},{"type":22,"tag":896,"props":16607,"children":16608},{"style":903},[16609],{"type":27,"value":1649},{"type":22,"tag":896,"props":16611,"children":16612},{"class":898,"line":755},[16613,16617,16622,16626,16631,16635,16640,16645,16649,16653],{"type":22,"tag":896,"props":16614,"children":16615},{"style":1691},[16616],{"type":27,"value":14636},{"type":22,"tag":896,"props":16618,"children":16619},{"style":903},[16620],{"type":27,"value":16621}," (tp ",{"type":22,"tag":896,"props":16623,"children":16624},{"style":1691},[16625],{"type":27,"value":14227},{"type":22,"tag":896,"props":16627,"children":16628},{"style":903},[16629],{"type":27,"value":16630}," buf ",{"type":22,"tag":896,"props":16632,"children":16633},{"style":1691},[16634],{"type":27,"value":3713},{"type":22,"tag":896,"props":16636,"children":16637},{"style":1691},[16638],{"type":27,"value":16639}," sizeof",{"type":22,"tag":896,"props":16641,"children":16642},{"style":903},[16643],{"type":27,"value":16644},"(buf) ",{"type":22,"tag":896,"props":16646,"children":16647},{"style":1691},[16648],{"type":27,"value":4207},{"type":22,"tag":896,"props":16650,"children":16651},{"style":1632},[16652],{"type":27,"value":1688},{"type":22,"tag":896,"props":16654,"children":16655},{"style":903},[16656],{"type":27,"value":6377},{"type":22,"tag":896,"props":16658,"children":16659},{"class":898,"line":983},[16660,16665,16669],{"type":22,"tag":896,"props":16661,"children":16662},{"style":1691},[16663],{"type":27,"value":16664},"#if",{"type":22,"tag":896,"props":16666,"children":16667},{"style":1632},[16668],{"type":27,"value":2933},{"type":22,"tag":896,"props":16670,"children":16671},{"style":2070},[16672],{"type":27,"value":16673},"  /* don't syslog for now - DoS potential on filling syslog */\n",{"type":22,"tag":896,"props":16675,"children":16676},{"class":898,"line":1013},[16677],{"type":22,"tag":896,"props":16678,"children":16679},{"style":2070},[16680],{"type":27,"value":16681},"                msyslog(LOG_WARNING,\n",{"type":22,"tag":896,"props":16683,"children":16684},{"class":898,"line":1030},[16685],{"type":22,"tag":896,"props":16686,"children":16687},{"style":2070},[16688],{"type":27,"value":16689},"                    \"Attempted \\\"ntpdx\\\" exploit from IP %d.%d.%d.%d:%d (possibly spoofed)\\n\",\n",{"type":22,"tag":896,"props":16691,"children":16692},{"class":898,"line":1068},[16693],{"type":22,"tag":896,"props":16694,"children":16695},{"style":2070},[16696],{"type":27,"value":16697},"                    (ntohl(rmt_addr->sin_addr.s_addr) >> 24) & 0xff,\n",{"type":22,"tag":896,"props":16699,"children":16700},{"class":898,"line":1085},[16701],{"type":22,"tag":896,"props":16702,"children":16703},{"style":2070},[16704],{"type":27,"value":16705},"                    (ntohl(rmt_addr->sin_addr.s_addr) >> 16) & 0xff,\n",{"type":22,"tag":896,"props":16707,"children":16708},{"class":898,"line":1123},[16709],{"type":22,"tag":896,"props":16710,"children":16711},{"style":2070},[16712],{"type":27,"value":16713},"                    (ntohl(rmt_addr->sin_addr.s_addr) >> 8) & 0xff,\n",{"type":22,"tag":896,"props":16715,"children":16716},{"class":898,"line":1161},[16717],{"type":22,"tag":896,"props":16718,"children":16719},{"style":2070},[16720],{"type":27,"value":16721},"                    (ntohl(rmt_addr->sin_addr.s_addr) >> 0) & 0xff,\n",{"type":22,"tag":896,"props":16723,"children":16724},{"class":898,"line":2298},[16725],{"type":22,"tag":896,"props":16726,"children":16727},{"style":2070},[16728],{"type":27,"value":16729},"                    ntohs(rmt_addr->sin_port));\n",{"type":22,"tag":896,"props":16731,"children":16732},{"class":898,"line":3170},[16733],{"type":22,"tag":896,"props":16734,"children":16735},{"style":1691},[16736],{"type":27,"value":16737},"#endif\n",{"type":22,"tag":896,"props":16739,"children":16740},{"class":898,"line":3187},[16741,16746,16750,16754],{"type":22,"tag":896,"props":16742,"children":16743},{"style":1691},[16744],{"type":27,"value":16745},"                return",{"type":22,"tag":896,"props":16747,"children":16748},{"style":903},[16749],{"type":27,"value":3643},{"type":22,"tag":896,"props":16751,"children":16752},{"style":1632},[16753],{"type":27,"value":9362},{"type":22,"tag":896,"props":16755,"children":16756},{"style":903},[16757],{"type":27,"value":3678},{"type":22,"tag":896,"props":16759,"children":16760},{"class":898,"line":3228},[16761],{"type":22,"tag":896,"props":16762,"children":16763},{"style":903},[16764],{"type":27,"value":14621},{"type":22,"tag":896,"props":16766,"children":16767},{"class":898,"line":3245},[16768],{"type":22,"tag":896,"props":16769,"children":16770},{"style":903},[16771],{"type":27,"value":7177},{"type":22,"tag":23,"props":16773,"children":16774},{},[16775],{"type":27,"value":16776},"It worth appreciating how the potential exploitability of the fix was itself taken into consideration, by choosing not to write logs when the size check is triggered. NTP is a UDP-based protocol which means that source IP addresses can be spoofed by the sender, so it was decided that there wasn't enough value in logging potentially-spoofed IPs given that it potentially introduces a resource exhaustion denial-of-service attack (by deliberately filling up the server's logs).",{"type":22,"tag":193,"props":16778,"children":16780},{"id":16779},"takeaways",[16781],{"type":27,"value":16782},"Takeaways",{"type":22,"tag":111,"props":16784,"children":16785},{},[16786,16791,16796],{"type":22,"tag":115,"props":16787,"children":16788},{},[16789],{"type":27,"value":16790},"Copying untrusted data in memory-unsafe languages is a risky process which must be done  with the utmost care.",{"type":22,"tag":115,"props":16792,"children":16793},{},[16794],{"type":27,"value":16795},"When code which performs a copy is being a reviewed, a useful technique is to step through each code path leading up to the loop and assess whether any of them could cause the copy to overflow the buffer.",{"type":22,"tag":115,"props":16797,"children":16798},{},[16799,16801,16807,16809,16815],{"type":27,"value":16800},"Whenever possible, \"safe\" size-aware copying routines should be used (e.g. ",{"type":22,"tag":486,"props":16802,"children":16804},{"className":16803},[],[16805],{"type":27,"value":16806},"strncpy",{"type":27,"value":16808},") in place of \"unsafe\" routines (e.g. ",{"type":22,"tag":486,"props":16810,"children":16812},{"className":16811},[],[16813],{"type":27,"value":16814},"strcpy",{"type":27,"value":16816},") or ad-hoc code.",{"type":22,"tag":1999,"props":16818,"children":16819},{},[16820],{"type":27,"value":2457},{"title":8,"searchDepth":755,"depth":755,"links":16822},[16823,16824,16825,16826,16827],{"id":13503,"depth":758,"text":13506},{"id":13773,"depth":758,"text":13776},{"id":15477,"depth":758,"text":15480},{"id":16509,"depth":758,"text":16512},{"id":16779,"depth":758,"text":16782},"content:phendry:2021-10-30:SpotTheVulnLoopsAndTermConditions.md","phendry/2021-10-30/SpotTheVulnLoopsAndTermConditions.md","phendry/2021-10-30/SpotTheVulnLoopsAndTermConditions",{"user":774,"name":775},{"_path":16833,"_dir":16834,"_draft":7,"_partial":7,"_locale":8,"title":16835,"description":16836,"tags":16837,"image":16838,"publishDate":16834,"excerpt":16836,"body":16839,"_type":767,"_id":18895,"_source":769,"_file":18896,"_stem":18897,"_extension":772,"author":18898},"/phendry/2021-08-15/exploringdependenttypesinidris","2021-08-15","Exploring Dependent Types in Idris","When I'm not coding the \"impossible\" at Art+Logic, I take a lot of interest in new programming technologies and paradigms; even if they're not yet viable for use in production, there can often be takeaways for improving your everyday code.",[12],"/phendry/2021-08-15/img/dependent-types.jpg",{"type":19,"children":16840,"toc":18885},[16841,16845,16859,16865,16884,16889,16925,16961,17033,17038,17100,17105,17117,17202,17237,17263,17269,17282,17412,17417,17529,17580,17606,17809,17822,17940,17945,18107,18126,18145,18263,18268,18277,18304,18309,18448,18454,18466,18487,18492,18498,18503,18509,18514,18520,18531,18551,18613,18642,18815,18849,18854,18860,18881],{"type":22,"tag":23,"props":16842,"children":16843},{},[16844],{"type":27,"value":16836},{"type":22,"tag":23,"props":16846,"children":16847},{},[16848,16850,16857],{"type":27,"value":16849},"My current fascination is the ",{"type":22,"tag":30,"props":16851,"children":16854},{"href":16852,"rel":16853},"https://www.idris-lang.org/",[34],[16855],{"type":27,"value":16856},"Idris",{"type":27,"value":16858}," programming language, a research language built around making dependent types practical. This is a quick primer on what dependent types are, how they work in Idris, and how they can change the way you think about types in other languages; we'll assume no prior knowledge of Idris or of purely functional languages in general, but a basic familiarity with functional programming will make things easier to follow.",{"type":22,"tag":193,"props":16860,"children":16862},{"id":16861},"what-is-a-dependent-type",[16863],{"type":27,"value":16864},"What is a Dependent Type?",{"type":22,"tag":23,"props":16866,"children":16867},{},[16868,16870,16875,16877,16882],{"type":27,"value":16869},"A ",{"type":22,"tag":267,"props":16871,"children":16872},{},[16873],{"type":27,"value":16874},"dependent type",{"type":27,"value":16876}," is a type whose definition depends on a value. More generally, Idris has ",{"type":22,"tag":267,"props":16878,"children":16879},{},[16880],{"type":27,"value":16881},"first-class types",{"type":27,"value":16883},", meaning that types can be computed and manipulated like any other language construct (e.g. functions or values).",{"type":22,"tag":23,"props":16885,"children":16886},{},[16887],{"type":27,"value":16888},"In Idris for example, it's no problem to define a function with a type like this:",{"type":22,"tag":886,"props":16890,"children":16894},{"className":16891,"code":16892,"language":16893,"meta":8,"style":8},"language-haskell shiki shiki-themes github-light github-dark","stringOrInt : Bool -> Type\n","haskell",[16895],{"type":22,"tag":486,"props":16896,"children":16897},{"__ignoreMap":8},[16898],{"type":22,"tag":896,"props":16899,"children":16900},{"class":898,"line":899},[16901,16906,16910,16915,16920],{"type":22,"tag":896,"props":16902,"children":16903},{"style":903},[16904],{"type":27,"value":16905},"stringOrInt ",{"type":22,"tag":896,"props":16907,"children":16908},{"style":1691},[16909],{"type":27,"value":6801},{"type":22,"tag":896,"props":16911,"children":16912},{"style":1632},[16913],{"type":27,"value":16914}," Bool",{"type":22,"tag":896,"props":16916,"children":16917},{"style":1691},[16918],{"type":27,"value":16919}," ->",{"type":22,"tag":896,"props":16921,"children":16922},{"style":1632},[16923],{"type":27,"value":16924}," Type\n",{"type":22,"tag":23,"props":16926,"children":16927},{},[16928,16929,16935,16937,16943,16945,16951,16953,16959],{"type":27,"value":10237},{"type":22,"tag":486,"props":16930,"children":16932},{"className":16931},[],[16933],{"type":27,"value":16934},"stringOrInt :",{"type":27,"value":16936}," notation begins a type definition, and ",{"type":22,"tag":486,"props":16938,"children":16940},{"className":16939},[],[16941],{"type":27,"value":16942},"Bool -> Type",{"type":27,"value":16944}," defines a function of one parameter (",{"type":22,"tag":486,"props":16946,"children":16948},{"className":16947},[],[16949],{"type":27,"value":16950},"Bool",{"type":27,"value":16952},") with a return type of ",{"type":22,"tag":486,"props":16954,"children":16956},{"className":16955},[],[16957],{"type":27,"value":16958},"Type",{"type":27,"value":16960},". It could be implemented like so:",{"type":22,"tag":886,"props":16962,"children":16964},{"className":16891,"code":16963,"language":16893,"meta":8,"style":8},"stringOrInt : Bool -> Type\nstringOrInt False = String\nstringOrInt True = Int\n",[16965],{"type":22,"tag":486,"props":16966,"children":16967},{"__ignoreMap":8},[16968,16991,17012],{"type":22,"tag":896,"props":16969,"children":16970},{"class":898,"line":899},[16971,16975,16979,16983,16987],{"type":22,"tag":896,"props":16972,"children":16973},{"style":903},[16974],{"type":27,"value":16905},{"type":22,"tag":896,"props":16976,"children":16977},{"style":1691},[16978],{"type":27,"value":6801},{"type":22,"tag":896,"props":16980,"children":16981},{"style":1632},[16982],{"type":27,"value":16914},{"type":22,"tag":896,"props":16984,"children":16985},{"style":1691},[16986],{"type":27,"value":16919},{"type":22,"tag":896,"props":16988,"children":16989},{"style":1632},[16990],{"type":27,"value":16924},{"type":22,"tag":896,"props":16992,"children":16993},{"class":898,"line":758},[16994,16998,17003,17007],{"type":22,"tag":896,"props":16995,"children":16996},{"style":903},[16997],{"type":27,"value":16905},{"type":22,"tag":896,"props":16999,"children":17000},{"style":1632},[17001],{"type":27,"value":17002},"False",{"type":22,"tag":896,"props":17004,"children":17005},{"style":1691},[17006],{"type":27,"value":3638},{"type":22,"tag":896,"props":17008,"children":17009},{"style":1632},[17010],{"type":27,"value":17011}," String\n",{"type":22,"tag":896,"props":17013,"children":17014},{"class":898,"line":755},[17015,17019,17024,17028],{"type":22,"tag":896,"props":17016,"children":17017},{"style":903},[17018],{"type":27,"value":16905},{"type":22,"tag":896,"props":17020,"children":17021},{"style":1632},[17022],{"type":27,"value":17023},"True",{"type":22,"tag":896,"props":17025,"children":17026},{"style":1691},[17027],{"type":27,"value":3638},{"type":22,"tag":896,"props":17029,"children":17030},{"style":1632},[17031],{"type":27,"value":17032}," Int\n",{"type":22,"tag":23,"props":17034,"children":17035},{},[17036],{"type":27,"value":17037},"Idris (and Haskell, the language its syntax is based on) lets you implement a function by pattern-matching on its arguments. So the full definition above can be understood as",{"type":22,"tag":847,"props":17039,"children":17040},{},[17041,17066,17084],{"type":22,"tag":115,"props":17042,"children":17043},{},[17044,17046,17052,17054,17059,17061],{"type":27,"value":17045},"Define a function ",{"type":22,"tag":486,"props":17047,"children":17049},{"className":17048},[],[17050],{"type":27,"value":17051},"stringOrInt",{"type":27,"value":17053}," which takes a ",{"type":22,"tag":486,"props":17055,"children":17057},{"className":17056},[],[17058],{"type":27,"value":16950},{"type":27,"value":17060}," and returns a ",{"type":22,"tag":486,"props":17062,"children":17064},{"className":17063},[],[17065],{"type":27,"value":16958},{"type":22,"tag":115,"props":17067,"children":17068},{},[17069,17071,17076,17078],{"type":27,"value":17070},"If the argument matches ",{"type":22,"tag":486,"props":17072,"children":17074},{"className":17073},[],[17075],{"type":27,"value":17002},{"type":27,"value":17077},", then return ",{"type":22,"tag":486,"props":17079,"children":17081},{"className":17080},[],[17082],{"type":27,"value":17083},"String",{"type":22,"tag":115,"props":17085,"children":17086},{},[17087,17088,17093,17094],{"type":27,"value":17070},{"type":22,"tag":486,"props":17089,"children":17091},{"className":17090},[],[17092],{"type":27,"value":17023},{"type":27,"value":17077},{"type":22,"tag":486,"props":17095,"children":17097},{"className":17096},[],[17098],{"type":27,"value":17099},"Int",{"type":22,"tag":23,"props":17101,"children":17102},{},[17103],{"type":27,"value":17104},"The compiler is smart enough to know that you've covered all possible cases in the pattern match. This is how functions are defined in a purely functional language: declaratively, by describing the expression that calculates the result (rather than by describing a sequence of imperative steps directly).",{"type":22,"tag":23,"props":17106,"children":17107},{},[17108,17110,17115],{"type":27,"value":17109},"So what we've got is a function which returns not a value but a ",{"type":22,"tag":267,"props":17111,"children":17112},{},[17113],{"type":27,"value":17114},"type",{"type":27,"value":17116},", something which isn't supposed to even exist at runtime. What good is that? Well, we can use it in the type of another definition to do something that would be totally crazy in almost any other language:",{"type":22,"tag":886,"props":17118,"children":17120},{"className":16891,"code":17119,"language":16893,"meta":8,"style":8},"dependent : (x : Bool) -> stringOrInt x\ndependent False = \"a string\"\ndependent True = 123\n",[17121],{"type":22,"tag":486,"props":17122,"children":17123},{"__ignoreMap":8},[17124,17162,17182],{"type":22,"tag":896,"props":17125,"children":17126},{"class":898,"line":899},[17127,17132,17136,17141,17145,17149,17153,17157],{"type":22,"tag":896,"props":17128,"children":17129},{"style":903},[17130],{"type":27,"value":17131},"dependent ",{"type":22,"tag":896,"props":17133,"children":17134},{"style":1691},[17135],{"type":27,"value":6801},{"type":22,"tag":896,"props":17137,"children":17138},{"style":903},[17139],{"type":27,"value":17140}," (x ",{"type":22,"tag":896,"props":17142,"children":17143},{"style":1691},[17144],{"type":27,"value":6801},{"type":22,"tag":896,"props":17146,"children":17147},{"style":1632},[17148],{"type":27,"value":16914},{"type":22,"tag":896,"props":17150,"children":17151},{"style":903},[17152],{"type":27,"value":6082},{"type":22,"tag":896,"props":17154,"children":17155},{"style":1691},[17156],{"type":27,"value":15573},{"type":22,"tag":896,"props":17158,"children":17159},{"style":903},[17160],{"type":27,"value":17161}," stringOrInt x\n",{"type":22,"tag":896,"props":17163,"children":17164},{"class":898,"line":758},[17165,17169,17173,17177],{"type":22,"tag":896,"props":17166,"children":17167},{"style":903},[17168],{"type":27,"value":17131},{"type":22,"tag":896,"props":17170,"children":17171},{"style":1632},[17172],{"type":27,"value":17002},{"type":22,"tag":896,"props":17174,"children":17175},{"style":1691},[17176],{"type":27,"value":3638},{"type":22,"tag":896,"props":17178,"children":17179},{"style":944},[17180],{"type":27,"value":17181}," \"a string\"\n",{"type":22,"tag":896,"props":17183,"children":17184},{"class":898,"line":755},[17185,17189,17193,17197],{"type":22,"tag":896,"props":17186,"children":17187},{"style":903},[17188],{"type":27,"value":17131},{"type":22,"tag":896,"props":17190,"children":17191},{"style":1632},[17192],{"type":27,"value":17023},{"type":22,"tag":896,"props":17194,"children":17195},{"style":1691},[17196],{"type":27,"value":3638},{"type":22,"tag":896,"props":17198,"children":17199},{"style":1632},[17200],{"type":27,"value":17201}," 123\n",{"type":22,"tag":23,"props":17203,"children":17204},{},[17205,17206,17212,17214,17220,17222,17228,17230,17235],{"type":27,"value":10237},{"type":22,"tag":486,"props":17207,"children":17209},{"className":17208},[],[17210],{"type":27,"value":17211},"(x : Bool)",{"type":27,"value":17213}," syntax here gives the name ",{"type":22,"tag":486,"props":17215,"children":17217},{"className":17216},[],[17218],{"type":27,"value":17219},"x",{"type":27,"value":17221}," to the parameter, so that it can be referred to in the return type. This ",{"type":22,"tag":486,"props":17223,"children":17225},{"className":17224},[],[17226],{"type":27,"value":17227},"dependent",{"type":27,"value":17229}," function returns ",{"type":22,"tag":267,"props":17231,"children":17232},{},[17233],{"type":27,"value":17234},"either",{"type":27,"value":17236}," a string or a number, depending on the input.",{"type":22,"tag":23,"props":17238,"children":17239},{},[17240,17242,17247,17249,17254,17256,17261],{"type":27,"value":17241},"In this example, ",{"type":22,"tag":486,"props":17243,"children":17245},{"className":17244},[],[17246],{"type":27,"value":17051},{"type":27,"value":17248}," is a dependent type: what it ends up being depends on the ",{"type":22,"tag":486,"props":17250,"children":17252},{"className":17251},[],[17253],{"type":27,"value":16950},{"type":27,"value":17255}," you provide to it. While ",{"type":22,"tag":486,"props":17257,"children":17259},{"className":17258},[],[17260],{"type":27,"value":17051},{"type":27,"value":17262}," might not be very useful, this concept enables some powerful capabilities.",{"type":22,"tag":193,"props":17264,"children":17266},{"id":17265},"why-dependent-types",[17267],{"type":27,"value":17268},"Why Dependent Types?",{"type":22,"tag":23,"props":17270,"children":17271},{},[17272,17274,17280],{"type":27,"value":17273},"A practical example of the power of dependent types is for defining a data type for lists of a specific length. In Idris, a ",{"type":22,"tag":486,"props":17275,"children":17277},{"className":17276},[],[17278],{"type":27,"value":17279},"Vect",{"type":27,"value":17281}," type can be defined like so:",{"type":22,"tag":886,"props":17283,"children":17285},{"className":16891,"code":17284,"language":16893,"meta":8,"style":8},"data Vect : Nat -> Type -> Type where\n   Nil  : Vect 0 t\n   Cons : t -> Vect k t -> Vect (S k) t\n",[17286],{"type":22,"tag":486,"props":17287,"children":17288},{"__ignoreMap":8},[17289,17334,17360],{"type":22,"tag":896,"props":17290,"children":17291},{"class":898,"line":899},[17292,17297,17302,17307,17312,17316,17321,17325,17329],{"type":22,"tag":896,"props":17293,"children":17294},{"style":1691},[17295],{"type":27,"value":17296},"data",{"type":22,"tag":896,"props":17298,"children":17299},{"style":1691},[17300],{"type":27,"value":17301}," Vect",{"type":22,"tag":896,"props":17303,"children":17304},{"style":1691},[17305],{"type":27,"value":17306}," :",{"type":22,"tag":896,"props":17308,"children":17309},{"style":1691},[17310],{"type":27,"value":17311}," Nat",{"type":22,"tag":896,"props":17313,"children":17314},{"style":1691},[17315],{"type":27,"value":16919},{"type":22,"tag":896,"props":17317,"children":17318},{"style":1691},[17319],{"type":27,"value":17320}," Type",{"type":22,"tag":896,"props":17322,"children":17323},{"style":1691},[17324],{"type":27,"value":16919},{"type":22,"tag":896,"props":17326,"children":17327},{"style":1691},[17328],{"type":27,"value":17320},{"type":22,"tag":896,"props":17330,"children":17331},{"style":1691},[17332],{"type":27,"value":17333}," where\n",{"type":22,"tag":896,"props":17335,"children":17336},{"class":898,"line":758},[17337,17342,17347,17351,17355],{"type":22,"tag":896,"props":17338,"children":17339},{"style":1632},[17340],{"type":27,"value":17341},"   Nil",{"type":22,"tag":896,"props":17343,"children":17344},{"style":1691},[17345],{"type":27,"value":17346},"  :",{"type":22,"tag":896,"props":17348,"children":17349},{"style":1691},[17350],{"type":27,"value":17301},{"type":22,"tag":896,"props":17352,"children":17353},{"style":1632},[17354],{"type":27,"value":2933},{"type":22,"tag":896,"props":17356,"children":17357},{"style":903},[17358],{"type":27,"value":17359}," t\n",{"type":22,"tag":896,"props":17361,"children":17362},{"class":898,"line":755},[17363,17368,17372,17377,17381,17385,17390,17394,17398,17402,17407],{"type":22,"tag":896,"props":17364,"children":17365},{"style":1632},[17366],{"type":27,"value":17367},"   Cons",{"type":22,"tag":896,"props":17369,"children":17370},{"style":1691},[17371],{"type":27,"value":17306},{"type":22,"tag":896,"props":17373,"children":17374},{"style":903},[17375],{"type":27,"value":17376}," t ",{"type":22,"tag":896,"props":17378,"children":17379},{"style":1691},[17380],{"type":27,"value":15573},{"type":22,"tag":896,"props":17382,"children":17383},{"style":1691},[17384],{"type":27,"value":17301},{"type":22,"tag":896,"props":17386,"children":17387},{"style":903},[17388],{"type":27,"value":17389}," k t ",{"type":22,"tag":896,"props":17391,"children":17392},{"style":1691},[17393],{"type":27,"value":15573},{"type":22,"tag":896,"props":17395,"children":17396},{"style":1691},[17397],{"type":27,"value":17301},{"type":22,"tag":896,"props":17399,"children":17400},{"style":903},[17401],{"type":27,"value":3643},{"type":22,"tag":896,"props":17403,"children":17404},{"style":1691},[17405],{"type":27,"value":17406},"S",{"type":22,"tag":896,"props":17408,"children":17409},{"style":903},[17410],{"type":27,"value":17411}," k) t\n",{"type":22,"tag":23,"props":17413,"children":17414},{},[17415],{"type":27,"value":17416},"We'll gloss over the syntax here, so this can be understood as",{"type":22,"tag":847,"props":17418,"children":17419},{},[17420,17455,17475],{"type":22,"tag":115,"props":17421,"children":17422},{},[17423,17425,17431,17433,17439,17440,17446,17448,17453],{"type":27,"value":17424},"Define a new data type ",{"type":22,"tag":486,"props":17426,"children":17428},{"className":17427},[],[17429],{"type":27,"value":17430},"Vect k a",{"type":27,"value":17432},", where ",{"type":22,"tag":486,"props":17434,"children":17436},{"className":17435},[],[17437],{"type":27,"value":17438},"k",{"type":27,"value":13359},{"type":22,"tag":486,"props":17441,"children":17443},{"className":17442},[],[17444],{"type":27,"value":17445},"Nat",{"type":27,"value":17447}," (a natural number) and ",{"type":22,"tag":486,"props":17449,"children":17451},{"className":17450},[],[17452],{"type":27,"value":30},{"type":27,"value":17454}," is the type of the elements",{"type":22,"tag":115,"props":17456,"children":17457},{},[17458,17460,17466,17468,17473],{"type":27,"value":17459},"Define a constructor ",{"type":22,"tag":486,"props":17461,"children":17463},{"className":17462},[],[17464],{"type":27,"value":17465},"Nil",{"type":27,"value":17467}," to create an empty ",{"type":22,"tag":486,"props":17469,"children":17471},{"className":17470},[],[17472],{"type":27,"value":17279},{"type":27,"value":17474}," for any element type, of length 0",{"type":22,"tag":115,"props":17476,"children":17477},{},[17478,17479,17485,17487,17492,17494,17499,17500,17506,17508,17513,17514,17520,17522,17527],{"type":27,"value":17459},{"type":22,"tag":486,"props":17480,"children":17482},{"className":17481},[],[17483],{"type":27,"value":17484},"Cons x xs",{"type":27,"value":17486}," to prepend the element ",{"type":22,"tag":486,"props":17488,"children":17490},{"className":17489},[],[17491],{"type":27,"value":17219},{"type":27,"value":17493}," to another ",{"type":22,"tag":486,"props":17495,"children":17497},{"className":17496},[],[17498],{"type":27,"value":17279},{"type":27,"value":13057},{"type":22,"tag":486,"props":17501,"children":17503},{"className":17502},[],[17504],{"type":27,"value":17505},"xs",{"type":27,"value":17507},", adding 1 to the length of the ",{"type":22,"tag":486,"props":17509,"children":17511},{"className":17510},[],[17512],{"type":27,"value":17279},{"type":27,"value":3643},{"type":22,"tag":486,"props":17515,"children":17517},{"className":17516},[],[17518],{"type":27,"value":17519},"(S k)",{"type":27,"value":17521}," here meaning \"add 1 to ",{"type":22,"tag":486,"props":17523,"children":17525},{"className":17524},[],[17526],{"type":27,"value":17438},{"type":27,"value":17528},"\")",{"type":22,"tag":23,"props":17530,"children":17531},{},[17532,17534,17540,17542,17548,17550,17556,17557,17563,17565,17571,17573,17578],{"type":27,"value":17533},"This ",{"type":22,"tag":486,"props":17535,"children":17537},{"className":17536},[],[17538],{"type":27,"value":17539},"Vect n t",{"type":27,"value":17541}," type has something in common with generic array types like TypeScript's ",{"type":22,"tag":486,"props":17543,"children":17545},{"className":17544},[],[17546],{"type":27,"value":17547},"Array\u003CT>",{"type":27,"value":17549},": they each have a ",{"type":22,"tag":486,"props":17551,"children":17553},{"className":17552},[],[17554],{"type":27,"value":17555},"t",{"type":27,"value":2684},{"type":22,"tag":486,"props":17558,"children":17560},{"className":17559},[],[17561],{"type":27,"value":17562},"T",{"type":27,"value":17564}," parameter to define the type of the array elements (although since TypeScript's types aren't first-class, it appears in the special ",{"type":22,"tag":486,"props":17566,"children":17568},{"className":17567},[],[17569],{"type":27,"value":17570},"\u003C>",{"type":27,"value":17572}," syntax rather than being a regular old function parameter). What ",{"type":22,"tag":486,"props":17574,"children":17576},{"className":17575},[],[17577],{"type":27,"value":17279},{"type":27,"value":17579}," adds is a value which encodes the length of the list right in the type itself.",{"type":22,"tag":23,"props":17581,"children":17582},{},[17583,17585,17590,17592,17597,17598,17604],{"type":27,"value":17584},"With this definition in hand, we can define ",{"type":22,"tag":486,"props":17586,"children":17588},{"className":17587},[],[17589],{"type":27,"value":17279},{"type":27,"value":17591}," values using the ",{"type":22,"tag":486,"props":17593,"children":17595},{"className":17594},[],[17596],{"type":27,"value":17465},{"type":27,"value":1441},{"type":22,"tag":486,"props":17599,"children":17601},{"className":17600},[],[17602],{"type":27,"value":17603},"Cons",{"type":27,"value":17605}," constructors:",{"type":22,"tag":886,"props":17607,"children":17609},{"className":16891,"code":17608,"language":16893,"meta":8,"style":8},"-- An Int Vect of length 0\nnoInts : Vect Z Int\nnoInts = Nil\n\n-- A Bool Vect of length 2\ntwoBools : Vect 2 Bool\ntwoBools = Cons True (Cons False Nil)\n\n-- A Bool Vect of length 3, built by adding a third element to twoBools\nthreeBools : Vect 3 Bool\nthreeBools = Cons True twoBools\n",[17610],{"type":22,"tag":486,"props":17611,"children":17612},{"__ignoreMap":8},[17613,17621,17646,17662,17669,17677,17702,17745,17752,17760,17785],{"type":22,"tag":896,"props":17614,"children":17615},{"class":898,"line":899},[17616],{"type":22,"tag":896,"props":17617,"children":17618},{"style":2070},[17619],{"type":27,"value":17620},"-- An Int Vect of length 0\n",{"type":22,"tag":896,"props":17622,"children":17623},{"class":898,"line":758},[17624,17629,17633,17637,17642],{"type":22,"tag":896,"props":17625,"children":17626},{"style":903},[17627],{"type":27,"value":17628},"noInts ",{"type":22,"tag":896,"props":17630,"children":17631},{"style":1691},[17632],{"type":27,"value":6801},{"type":22,"tag":896,"props":17634,"children":17635},{"style":1632},[17636],{"type":27,"value":17301},{"type":22,"tag":896,"props":17638,"children":17639},{"style":1632},[17640],{"type":27,"value":17641}," Z",{"type":22,"tag":896,"props":17643,"children":17644},{"style":1632},[17645],{"type":27,"value":17032},{"type":22,"tag":896,"props":17647,"children":17648},{"class":898,"line":755},[17649,17653,17657],{"type":22,"tag":896,"props":17650,"children":17651},{"style":903},[17652],{"type":27,"value":17628},{"type":22,"tag":896,"props":17654,"children":17655},{"style":1691},[17656],{"type":27,"value":941},{"type":22,"tag":896,"props":17658,"children":17659},{"style":1632},[17660],{"type":27,"value":17661}," Nil\n",{"type":22,"tag":896,"props":17663,"children":17664},{"class":898,"line":983},[17665],{"type":22,"tag":896,"props":17666,"children":17667},{"emptyLinePlaceholder":3481},[17668],{"type":27,"value":3484},{"type":22,"tag":896,"props":17670,"children":17671},{"class":898,"line":1013},[17672],{"type":22,"tag":896,"props":17673,"children":17674},{"style":2070},[17675],{"type":27,"value":17676},"-- A Bool Vect of length 2\n",{"type":22,"tag":896,"props":17678,"children":17679},{"class":898,"line":1030},[17680,17685,17689,17693,17697],{"type":22,"tag":896,"props":17681,"children":17682},{"style":903},[17683],{"type":27,"value":17684},"twoBools ",{"type":22,"tag":896,"props":17686,"children":17687},{"style":1691},[17688],{"type":27,"value":6801},{"type":22,"tag":896,"props":17690,"children":17691},{"style":1632},[17692],{"type":27,"value":17301},{"type":22,"tag":896,"props":17694,"children":17695},{"style":1632},[17696],{"type":27,"value":7144},{"type":22,"tag":896,"props":17698,"children":17699},{"style":1632},[17700],{"type":27,"value":17701}," Bool\n",{"type":22,"tag":896,"props":17703,"children":17704},{"class":898,"line":1068},[17705,17709,17713,17718,17723,17727,17731,17736,17741],{"type":22,"tag":896,"props":17706,"children":17707},{"style":903},[17708],{"type":27,"value":17684},{"type":22,"tag":896,"props":17710,"children":17711},{"style":1691},[17712],{"type":27,"value":941},{"type":22,"tag":896,"props":17714,"children":17715},{"style":1632},[17716],{"type":27,"value":17717}," Cons",{"type":22,"tag":896,"props":17719,"children":17720},{"style":1632},[17721],{"type":27,"value":17722}," True",{"type":22,"tag":896,"props":17724,"children":17725},{"style":903},[17726],{"type":27,"value":3643},{"type":22,"tag":896,"props":17728,"children":17729},{"style":1632},[17730],{"type":27,"value":17603},{"type":22,"tag":896,"props":17732,"children":17733},{"style":1632},[17734],{"type":27,"value":17735}," False",{"type":22,"tag":896,"props":17737,"children":17738},{"style":1632},[17739],{"type":27,"value":17740}," Nil",{"type":22,"tag":896,"props":17742,"children":17743},{"style":903},[17744],{"type":27,"value":2903},{"type":22,"tag":896,"props":17746,"children":17747},{"class":898,"line":1085},[17748],{"type":22,"tag":896,"props":17749,"children":17750},{"emptyLinePlaceholder":3481},[17751],{"type":27,"value":3484},{"type":22,"tag":896,"props":17753,"children":17754},{"class":898,"line":1123},[17755],{"type":22,"tag":896,"props":17756,"children":17757},{"style":2070},[17758],{"type":27,"value":17759},"-- A Bool Vect of length 3, built by adding a third element to twoBools\n",{"type":22,"tag":896,"props":17761,"children":17762},{"class":898,"line":1161},[17763,17768,17772,17776,17781],{"type":22,"tag":896,"props":17764,"children":17765},{"style":903},[17766],{"type":27,"value":17767},"threeBools ",{"type":22,"tag":896,"props":17769,"children":17770},{"style":1691},[17771],{"type":27,"value":6801},{"type":22,"tag":896,"props":17773,"children":17774},{"style":1632},[17775],{"type":27,"value":17301},{"type":22,"tag":896,"props":17777,"children":17778},{"style":1632},[17779],{"type":27,"value":17780}," 3",{"type":22,"tag":896,"props":17782,"children":17783},{"style":1632},[17784],{"type":27,"value":17701},{"type":22,"tag":896,"props":17786,"children":17787},{"class":898,"line":2298},[17788,17792,17796,17800,17804],{"type":22,"tag":896,"props":17789,"children":17790},{"style":903},[17791],{"type":27,"value":17767},{"type":22,"tag":896,"props":17793,"children":17794},{"style":1691},[17795],{"type":27,"value":941},{"type":22,"tag":896,"props":17797,"children":17798},{"style":1632},[17799],{"type":27,"value":17717},{"type":22,"tag":896,"props":17801,"children":17802},{"style":1632},[17803],{"type":27,"value":17722},{"type":22,"tag":896,"props":17805,"children":17806},{"style":903},[17807],{"type":27,"value":17808}," twoBools\n",{"type":22,"tag":23,"props":17810,"children":17811},{},[17812,17814,17820],{"type":27,"value":17813},"This length allows you to be much more specific in the type of, for instance, an ",{"type":22,"tag":486,"props":17815,"children":17817},{"className":17816},[],[17818],{"type":27,"value":17819},"append",{"type":27,"value":17821}," function:",{"type":22,"tag":886,"props":17823,"children":17825},{"className":16891,"code":17824,"language":16893,"meta":8,"style":8},"append : Vect n t -> Vect m t -> Vect (n + m) t\nappend Nil ys = ys\nappend (Cons x xs) ys = Cons x (append xs ys)\n",[17826],{"type":22,"tag":486,"props":17827,"children":17828},{"__ignoreMap":8},[17829,17885,17910],{"type":22,"tag":896,"props":17830,"children":17831},{"class":898,"line":899},[17832,17837,17841,17845,17850,17854,17858,17863,17867,17871,17876,17880],{"type":22,"tag":896,"props":17833,"children":17834},{"style":903},[17835],{"type":27,"value":17836},"append ",{"type":22,"tag":896,"props":17838,"children":17839},{"style":1691},[17840],{"type":27,"value":6801},{"type":22,"tag":896,"props":17842,"children":17843},{"style":1632},[17844],{"type":27,"value":17301},{"type":22,"tag":896,"props":17846,"children":17847},{"style":903},[17848],{"type":27,"value":17849}," n t ",{"type":22,"tag":896,"props":17851,"children":17852},{"style":1691},[17853],{"type":27,"value":15573},{"type":22,"tag":896,"props":17855,"children":17856},{"style":1632},[17857],{"type":27,"value":17301},{"type":22,"tag":896,"props":17859,"children":17860},{"style":903},[17861],{"type":27,"value":17862}," m t ",{"type":22,"tag":896,"props":17864,"children":17865},{"style":1691},[17866],{"type":27,"value":15573},{"type":22,"tag":896,"props":17868,"children":17869},{"style":1632},[17870],{"type":27,"value":17301},{"type":22,"tag":896,"props":17872,"children":17873},{"style":903},[17874],{"type":27,"value":17875}," (n ",{"type":22,"tag":896,"props":17877,"children":17878},{"style":1691},[17879],{"type":27,"value":3713},{"type":22,"tag":896,"props":17881,"children":17882},{"style":903},[17883],{"type":27,"value":17884}," m) t\n",{"type":22,"tag":896,"props":17886,"children":17887},{"class":898,"line":758},[17888,17892,17896,17901,17905],{"type":22,"tag":896,"props":17889,"children":17890},{"style":903},[17891],{"type":27,"value":17836},{"type":22,"tag":896,"props":17893,"children":17894},{"style":1632},[17895],{"type":27,"value":17465},{"type":22,"tag":896,"props":17897,"children":17898},{"style":903},[17899],{"type":27,"value":17900}," ys ",{"type":22,"tag":896,"props":17902,"children":17903},{"style":1691},[17904],{"type":27,"value":941},{"type":22,"tag":896,"props":17906,"children":17907},{"style":903},[17908],{"type":27,"value":17909}," ys\n",{"type":22,"tag":896,"props":17911,"children":17912},{"class":898,"line":755},[17913,17918,17922,17927,17931,17935],{"type":22,"tag":896,"props":17914,"children":17915},{"style":903},[17916],{"type":27,"value":17917},"append (",{"type":22,"tag":896,"props":17919,"children":17920},{"style":1632},[17921],{"type":27,"value":17603},{"type":22,"tag":896,"props":17923,"children":17924},{"style":903},[17925],{"type":27,"value":17926}," x xs) ys ",{"type":22,"tag":896,"props":17928,"children":17929},{"style":1691},[17930],{"type":27,"value":941},{"type":22,"tag":896,"props":17932,"children":17933},{"style":1632},[17934],{"type":27,"value":17717},{"type":22,"tag":896,"props":17936,"children":17937},{"style":903},[17938],{"type":27,"value":17939}," x (append xs ys)\n",{"type":22,"tag":23,"props":17941,"children":17942},{},[17943],{"type":27,"value":17944},"Let's break down what's going on here:",{"type":22,"tag":847,"props":17946,"children":17947},{},[17948,17995,18029,18054],{"type":22,"tag":115,"props":17949,"children":17950},{},[17951,17953,17958,17960,17965,17967,17973,17975,17980,17981,17987,17989,17994],{"type":27,"value":17952},"The type of ",{"type":22,"tag":486,"props":17954,"children":17956},{"className":17955},[],[17957],{"type":27,"value":17819},{"type":27,"value":17959}," says it takes two parameters, a ",{"type":22,"tag":486,"props":17961,"children":17963},{"className":17962},[],[17964],{"type":27,"value":17279},{"type":27,"value":17966}," of length ",{"type":22,"tag":486,"props":17968,"children":17970},{"className":17969},[],[17971],{"type":27,"value":17972},"n",{"type":27,"value":17974}," and a ",{"type":22,"tag":486,"props":17976,"children":17978},{"className":17977},[],[17979],{"type":27,"value":17279},{"type":27,"value":17966},{"type":22,"tag":486,"props":17982,"children":17984},{"className":17983},[],[17985],{"type":27,"value":17986},"m",{"type":27,"value":17988},", each of which have elements of the same type ",{"type":22,"tag":486,"props":17990,"children":17992},{"className":17991},[],[17993],{"type":27,"value":17555},{"type":27,"value":71},{"type":22,"tag":115,"props":17996,"children":17997},{},[17998,18000,18006,18008,18013,18015,18020,18022,18027],{"type":27,"value":17999},"The type says that the return type is ",{"type":22,"tag":486,"props":18001,"children":18003},{"className":18002},[],[18004],{"type":27,"value":18005},"Vect (n + m) t",{"type":27,"value":18007},", meaning it's a ",{"type":22,"tag":486,"props":18009,"children":18011},{"className":18010},[],[18012],{"type":27,"value":17279},{"type":27,"value":18014}," with the combined lengths of both inputs. We've got math going on ",{"type":22,"tag":267,"props":18016,"children":18017},{},[18018],{"type":27,"value":18019},"at the type level",{"type":27,"value":18021},", and Idris is OK with this: when we implement or use ",{"type":22,"tag":486,"props":18023,"children":18025},{"className":18024},[],[18026],{"type":27,"value":17819},{"type":27,"value":18028},", the compiler will evaluate this to make sure the code conforms. Crazy!",{"type":22,"tag":115,"props":18030,"children":18031},{},[18032,18034,18039,18041,18046,18048,18053],{"type":27,"value":18033},"If the first ",{"type":22,"tag":486,"props":18035,"children":18037},{"className":18036},[],[18038],{"type":27,"value":17279},{"type":27,"value":18040}," is ",{"type":22,"tag":486,"props":18042,"children":18044},{"className":18043},[],[18045],{"type":27,"value":17465},{"type":27,"value":18047}," (i.e. empty), then just return the second ",{"type":22,"tag":486,"props":18049,"children":18051},{"className":18050},[],[18052],{"type":27,"value":17279},{"type":27,"value":71},{"type":22,"tag":115,"props":18055,"children":18056},{},[18057,18059,18064,18066,18071,18073,18078,18079,18084,18086,18091,18093,18098,18100,18105],{"type":27,"value":18058},"Otherwise, the first ",{"type":22,"tag":486,"props":18060,"children":18062},{"className":18061},[],[18063],{"type":27,"value":17279},{"type":27,"value":18065}," must be an element ",{"type":22,"tag":486,"props":18067,"children":18069},{"className":18068},[],[18070],{"type":27,"value":17219},{"type":27,"value":18072}," prepended onto some other ",{"type":22,"tag":486,"props":18074,"children":18076},{"className":18075},[],[18077],{"type":27,"value":17279},{"type":27,"value":236},{"type":22,"tag":486,"props":18080,"children":18082},{"className":18081},[],[18083],{"type":27,"value":17505},{"type":27,"value":18085},". We can recursively ",{"type":22,"tag":486,"props":18087,"children":18089},{"className":18088},[],[18090],{"type":27,"value":17819},{"type":27,"value":18092}," the remaining ",{"type":22,"tag":486,"props":18094,"children":18096},{"className":18095},[],[18097],{"type":27,"value":17279},{"type":27,"value":18099},"s together and prepend ",{"type":22,"tag":486,"props":18101,"children":18103},{"className":18102},[],[18104],{"type":27,"value":17219},{"type":27,"value":18106}," onto the result.",{"type":22,"tag":23,"props":18108,"children":18109},{},[18110,18112,18117,18119,18124],{"type":27,"value":18111},"Using dependent types, we're able to have ",{"type":22,"tag":486,"props":18113,"children":18115},{"className":18114},[],[18116],{"type":27,"value":17819},{"type":27,"value":18118}," make a much stronger guarantee than it otherwise could: not only will it return a ",{"type":22,"tag":486,"props":18120,"children":18122},{"className":18121},[],[18123],{"type":27,"value":17279},{"type":27,"value":18125}," with the same type of elements as the inputs, but it will guarantee that the result has the correct length, or else the code won't even compile.",{"type":22,"tag":23,"props":18127,"children":18128},{},[18129,18131,18136,18138,18144],{"type":27,"value":18130},"Consider what would have happened if we had made a simple mistake and accidentally wrote a second ",{"type":22,"tag":486,"props":18132,"children":18134},{"className":18133},[],[18135],{"type":27,"value":17505},{"type":27,"value":18137}," instead of a ",{"type":22,"tag":486,"props":18139,"children":18141},{"className":18140},[],[18142],{"type":27,"value":18143},"ys",{"type":27,"value":6801},{"type":22,"tag":886,"props":18146,"children":18148},{"className":16891,"code":18147,"language":16893,"meta":8,"style":8},"append : Vect n t -> Vect m t -> Vect (n + m) t\nappend Nil ys = ys\nappend (Cons x xs) = Cons x (append xs xs)\n                                    -- ^ Oops, this should be ys\n",[18149],{"type":22,"tag":486,"props":18150,"children":18151},{"__ignoreMap":8},[18152,18203,18226,18255],{"type":22,"tag":896,"props":18153,"children":18154},{"class":898,"line":899},[18155,18159,18163,18167,18171,18175,18179,18183,18187,18191,18195,18199],{"type":22,"tag":896,"props":18156,"children":18157},{"style":903},[18158],{"type":27,"value":17836},{"type":22,"tag":896,"props":18160,"children":18161},{"style":1691},[18162],{"type":27,"value":6801},{"type":22,"tag":896,"props":18164,"children":18165},{"style":1632},[18166],{"type":27,"value":17301},{"type":22,"tag":896,"props":18168,"children":18169},{"style":903},[18170],{"type":27,"value":17849},{"type":22,"tag":896,"props":18172,"children":18173},{"style":1691},[18174],{"type":27,"value":15573},{"type":22,"tag":896,"props":18176,"children":18177},{"style":1632},[18178],{"type":27,"value":17301},{"type":22,"tag":896,"props":18180,"children":18181},{"style":903},[18182],{"type":27,"value":17862},{"type":22,"tag":896,"props":18184,"children":18185},{"style":1691},[18186],{"type":27,"value":15573},{"type":22,"tag":896,"props":18188,"children":18189},{"style":1632},[18190],{"type":27,"value":17301},{"type":22,"tag":896,"props":18192,"children":18193},{"style":903},[18194],{"type":27,"value":17875},{"type":22,"tag":896,"props":18196,"children":18197},{"style":1691},[18198],{"type":27,"value":3713},{"type":22,"tag":896,"props":18200,"children":18201},{"style":903},[18202],{"type":27,"value":17884},{"type":22,"tag":896,"props":18204,"children":18205},{"class":898,"line":758},[18206,18210,18214,18218,18222],{"type":22,"tag":896,"props":18207,"children":18208},{"style":903},[18209],{"type":27,"value":17836},{"type":22,"tag":896,"props":18211,"children":18212},{"style":1632},[18213],{"type":27,"value":17465},{"type":22,"tag":896,"props":18215,"children":18216},{"style":903},[18217],{"type":27,"value":17900},{"type":22,"tag":896,"props":18219,"children":18220},{"style":1691},[18221],{"type":27,"value":941},{"type":22,"tag":896,"props":18223,"children":18224},{"style":903},[18225],{"type":27,"value":17909},{"type":22,"tag":896,"props":18227,"children":18228},{"class":898,"line":755},[18229,18233,18237,18242,18246,18250],{"type":22,"tag":896,"props":18230,"children":18231},{"style":903},[18232],{"type":27,"value":17917},{"type":22,"tag":896,"props":18234,"children":18235},{"style":1632},[18236],{"type":27,"value":17603},{"type":22,"tag":896,"props":18238,"children":18239},{"style":903},[18240],{"type":27,"value":18241}," x xs) ",{"type":22,"tag":896,"props":18243,"children":18244},{"style":1691},[18245],{"type":27,"value":941},{"type":22,"tag":896,"props":18247,"children":18248},{"style":1632},[18249],{"type":27,"value":17717},{"type":22,"tag":896,"props":18251,"children":18252},{"style":903},[18253],{"type":27,"value":18254}," x (append xs xs)\n",{"type":22,"tag":896,"props":18256,"children":18257},{"class":898,"line":983},[18258],{"type":22,"tag":896,"props":18259,"children":18260},{"style":2070},[18261],{"type":27,"value":18262},"                                    -- ^ Oops, this should be ys\n",{"type":22,"tag":23,"props":18264,"children":18265},{},[18266],{"type":27,"value":18267},"This code will not compile, producing an error something like this:",{"type":22,"tag":886,"props":18269,"children":18272},{"className":18270,"code":18271,"language":27},[4580],"Error: While processing right hand side of append. When unifying `n + n` and `n + m`.\nMismatch between: `n` and `m`.\n\nExample:28:25--28:46\n 26 | append : Vect n t -> Vect m t -> Vect (n + m) t\n 27 | append Nil ys = ys\n 28 | append (Cons x xs) ys = Cons x (append xs xs)\n                              ^^^^^^^^^^^^^^^^^^^^^\n",[18273],{"type":22,"tag":486,"props":18274,"children":18275},{"__ignoreMap":8},[18276],{"type":27,"value":18271},{"type":22,"tag":23,"props":18278,"children":18279},{},[18280,18282,18288,18290,18295,18297,18302],{"type":27,"value":18281},"The compiler has identified that the type of the code we wrote is ",{"type":22,"tag":486,"props":18283,"children":18285},{"className":18284},[],[18286],{"type":27,"value":18287},"Vect (n + n) t",{"type":27,"value":18289},", but the definition of ",{"type":22,"tag":486,"props":18291,"children":18293},{"className":18292},[],[18294],{"type":27,"value":17819},{"type":27,"value":18296}," expects ",{"type":22,"tag":486,"props":18298,"children":18300},{"className":18299},[],[18301],{"type":27,"value":18005},{"type":27,"value":18303},", which doesn't match.",{"type":22,"tag":23,"props":18305,"children":18306},{},[18307],{"type":27,"value":18308},"This is a sample of the power of dependent types: making stronger guarantees at the type level, so that the compiler can catch and prevent many more errors and bugs. For the purposes of this overview we'll stop here, but there are a ton of other interesting applications, such as:",{"type":22,"tag":111,"props":18310,"children":18311},{},[18312,18329,18334,18339,18344],{"type":22,"tag":115,"props":18313,"children":18314},{},[18315],{"type":22,"tag":30,"props":18316,"children":18319},{"href":18317,"rel":18318},"https://www.youtube.com/watch?v=fVBck2Zngjo",[34],[18320,18322,18328],{"type":27,"value":18321},"A type-safe ",{"type":22,"tag":486,"props":18323,"children":18325},{"className":18324},[],[18326],{"type":27,"value":18327},"printf",{"type":27,"value":5566},{"type":22,"tag":115,"props":18330,"children":18331},{},[18332],{"type":27,"value":18333},"Defining state machines as data types, so that only valid state transitions are possible",{"type":22,"tag":115,"props":18335,"children":18336},{},[18337],{"type":27,"value":18338},"Allowing functions to make formal assertions about their inputs or outputs, such as \"the input array is sorted\"",{"type":22,"tag":115,"props":18340,"children":18341},{},[18342],{"type":27,"value":18343},"Using interactive editing to get the compiler to automatically fill in parts of your solution",{"type":22,"tag":115,"props":18345,"children":18346},{},[18347,18349],{"type":27,"value":18348},"Implementing your unit tests as proofs, e.g.:",{"type":22,"tag":886,"props":18350,"children":18352},{"className":16891,"code":18351,"language":16893,"meta":8,"style":8},"-- The type is an assertion that `length ['a', 'b']` evaluates to `2`.\n-- `Refl` asks the compiler to check that this is trivially true, by\n-- evaluating it down to `2 = 2`. The code gets executed as part of the\n-- compilation process, so if it compiles, then this \"unit test\" passes!\ntestLength2 : length ['a', 'b'] = 2\ntestLength2 = Refl\n",[18353],{"type":22,"tag":486,"props":18354,"children":18355},{"__ignoreMap":8},[18356,18364,18372,18380,18388,18432],{"type":22,"tag":896,"props":18357,"children":18358},{"class":898,"line":899},[18359],{"type":22,"tag":896,"props":18360,"children":18361},{"style":2070},[18362],{"type":27,"value":18363},"-- The type is an assertion that `length ['a', 'b']` evaluates to `2`.\n",{"type":22,"tag":896,"props":18365,"children":18366},{"class":898,"line":758},[18367],{"type":22,"tag":896,"props":18368,"children":18369},{"style":2070},[18370],{"type":27,"value":18371},"-- `Refl` asks the compiler to check that this is trivially true, by\n",{"type":22,"tag":896,"props":18373,"children":18374},{"class":898,"line":755},[18375],{"type":22,"tag":896,"props":18376,"children":18377},{"style":2070},[18378],{"type":27,"value":18379},"-- evaluating it down to `2 = 2`. The code gets executed as part of the\n",{"type":22,"tag":896,"props":18381,"children":18382},{"class":898,"line":983},[18383],{"type":22,"tag":896,"props":18384,"children":18385},{"style":2070},[18386],{"type":27,"value":18387},"-- compilation process, so if it compiles, then this \"unit test\" passes!\n",{"type":22,"tag":896,"props":18389,"children":18390},{"class":898,"line":1013},[18391,18396,18400,18405,18410,18414,18419,18423,18427],{"type":22,"tag":896,"props":18392,"children":18393},{"style":903},[18394],{"type":27,"value":18395},"testLength2 ",{"type":22,"tag":896,"props":18397,"children":18398},{"style":1691},[18399],{"type":27,"value":6801},{"type":22,"tag":896,"props":18401,"children":18402},{"style":903},[18403],{"type":27,"value":18404}," length [",{"type":22,"tag":896,"props":18406,"children":18407},{"style":944},[18408],{"type":27,"value":18409},"'a'",{"type":22,"tag":896,"props":18411,"children":18412},{"style":903},[18413],{"type":27,"value":236},{"type":22,"tag":896,"props":18415,"children":18416},{"style":944},[18417],{"type":27,"value":18418},"'b'",{"type":22,"tag":896,"props":18420,"children":18421},{"style":903},[18422],{"type":27,"value":7215},{"type":22,"tag":896,"props":18424,"children":18425},{"style":1691},[18426],{"type":27,"value":941},{"type":22,"tag":896,"props":18428,"children":18429},{"style":1632},[18430],{"type":27,"value":18431}," 2\n",{"type":22,"tag":896,"props":18433,"children":18434},{"class":898,"line":1030},[18435,18439,18443],{"type":22,"tag":896,"props":18436,"children":18437},{"style":903},[18438],{"type":27,"value":18395},{"type":22,"tag":896,"props":18440,"children":18441},{"style":1691},[18442],{"type":27,"value":941},{"type":22,"tag":896,"props":18444,"children":18445},{"style":1632},[18446],{"type":27,"value":18447}," Refl\n",{"type":22,"tag":193,"props":18449,"children":18451},{"id":18450},"why-not-dependent-types",[18452],{"type":27,"value":18453},"Why Not Dependent Types?",{"type":22,"tag":23,"props":18455,"children":18456},{},[18457,18459,18464],{"type":27,"value":18458},"With great power comes great responsibility, and first-class/dependent types introduce a ",{"type":22,"tag":267,"props":18460,"children":18461},{},[18462],{"type":27,"value":18463},"lot",{"type":27,"value":18465}," of power to a language. The catch with leaning on the compiler to make much stronger guarantees about your programs is that you need to convince the compiler that your code meets these guarantees, in rigorous, mathematical detail.",{"type":22,"tag":23,"props":18467,"children":18468},{},[18469,18471,18477,18479,18485],{"type":27,"value":18470},"For example, I've implemented a Binary Search Tree in Idris (see ",{"type":22,"tag":30,"props":18472,"children":18475},{"href":18473,"rel":18474},"https://github.com/polendri/idris-collections/blob/master/src/Collections/BSTree/Map/Core.idr",[34],[18476],{"type":27,"value":4369},{"type":27,"value":18478}," for the main data types, and ",{"type":22,"tag":30,"props":18480,"children":18483},{"href":18481,"rel":18482},"https://github.com/polendri/idris-collections/blob/master/src/Collections/BSTree/Map.idr",[34],[18484],{"type":27,"value":4369},{"type":27,"value":18486}," for the insertion/querying implementations), one which guarantees that any instance of a tree satisfies the ordering constraints. It works, but the implementation is definitely noisier and it took much, much longer to implement than it would have had I not given it a type with so many guarantees. As the code that you're formally verifying becomes more complex, implementation quickly goes from easy, to time-consuming, to an active research area.",{"type":22,"tag":23,"props":18488,"children":18489},{},[18490],{"type":27,"value":18491},"Finding language features and development practices which make dependent types easier to use is an exciting research area; you won't use Idris in production tomorrow, but... maybe someday.",{"type":22,"tag":193,"props":18493,"children":18495},{"id":18494},"practical-applications",[18496],{"type":27,"value":18497},"Practical Applications",{"type":22,"tag":23,"props":18499,"children":18500},{},[18501],{"type":27,"value":18502},"You may not be using Idris in production today, but taking some time to learn it might make you see your current code in a new light:",{"type":22,"tag":1481,"props":18504,"children":18506},{"id":18505},"the-power-of-types",[18507],{"type":27,"value":18508},"The Power of Types",{"type":22,"tag":23,"props":18510,"children":18511},{},[18512],{"type":27,"value":18513},"Early on in a new JavaScript, Python or Ruby project, the sheer speed of development might make the language feel far and away superior to a strictly typed language like C++, Java, or Rust. As the months and years go by however, and runtime errors keep cropping up, you may be longing for the guard rails that those types provide. Depending on the project's priorities, a strictly typed language may be a better investment in the long term. And keep in mind that with the backwards-compatible TypeScript and with Python's type annotations, sometimes a gradual transition is possible.",{"type":22,"tag":1481,"props":18515,"children":18517},{"id":18516},"the-value-of-descriptive-data-types",[18518],{"type":27,"value":18519},"The Value of Descriptive Data Types",{"type":22,"tag":23,"props":18521,"children":18522},{},[18523,18525,18529],{"type":27,"value":18524},"We might not be able to encode \"an array of length ",{"type":22,"tag":267,"props":18526,"children":18527},{},[18528],{"type":27,"value":17972},{"type":27,"value":18530},"\" in any popular programming language, or to get our compiler to formally guarantee that an array is properly sorted, but there can still be a lot of value in defining new types to encode these properties anyway.",{"type":22,"tag":23,"props":18532,"children":18533},{},[18534,18536,18541,18543,18549],{"type":27,"value":18535},"Take email addresses for example, which typically would be stored as a ",{"type":22,"tag":486,"props":18537,"children":18539},{"className":18538},[],[18540],{"type":27,"value":3923},{"type":27,"value":18542},". If your ",{"type":22,"tag":486,"props":18544,"children":18546},{"className":18545},[],[18547],{"type":27,"value":18548},"sendReminder",{"type":27,"value":18550}," function is implemented like this,",{"type":22,"tag":886,"props":18552,"children":18554},{"className":2854,"code":18553,"language":2856,"meta":8,"style":8},"void sendReminder(Appointment appointment, string email) { ... }\n",[18555],{"type":22,"tag":486,"props":18556,"children":18557},{"__ignoreMap":8},[18558],{"type":22,"tag":896,"props":18559,"children":18560},{"class":898,"line":899},[18561,18566,18571,18575,18580,18585,18589,18593,18598,18603,18608],{"type":22,"tag":896,"props":18562,"children":18563},{"style":1691},[18564],{"type":27,"value":18565},"void",{"type":22,"tag":896,"props":18567,"children":18568},{"style":933},[18569],{"type":27,"value":18570}," sendReminder",{"type":22,"tag":896,"props":18572,"children":18573},{"style":903},[18574],{"type":27,"value":2888},{"type":22,"tag":896,"props":18576,"children":18577},{"style":933},[18578],{"type":27,"value":18579},"Appointment",{"type":22,"tag":896,"props":18581,"children":18582},{"style":933},[18583],{"type":27,"value":18584}," appointment",{"type":22,"tag":896,"props":18586,"children":18587},{"style":903},[18588],{"type":27,"value":236},{"type":22,"tag":896,"props":18590,"children":18591},{"style":1691},[18592],{"type":27,"value":3923},{"type":22,"tag":896,"props":18594,"children":18595},{"style":933},[18596],{"type":27,"value":18597}," email",{"type":22,"tag":896,"props":18599,"children":18600},{"style":903},[18601],{"type":27,"value":18602},") { ",{"type":22,"tag":896,"props":18604,"children":18605},{"style":1691},[18606],{"type":27,"value":18607},"..",{"type":22,"tag":896,"props":18609,"children":18610},{"style":903},[18611],{"type":27,"value":18612},". }\n",{"type":22,"tag":23,"props":18614,"children":18615},{},[18616,18618,18624,18626,18632,18634,18640],{"type":27,"value":18617},"then any number of bugs could cause the value of ",{"type":22,"tag":486,"props":18619,"children":18621},{"className":18620},[],[18622],{"type":27,"value":18623},"email",{"type":27,"value":18625}," to be something which is not an email address. This is unfortunate, because the error is going to occur somewhere inside ",{"type":22,"tag":486,"props":18627,"children":18629},{"className":18628},[],[18630],{"type":27,"value":18631},"sendReminder()",{"type":27,"value":18633},", leaving you to go on a hunt for the true source of the bug. Consider however if you were to define an ",{"type":22,"tag":486,"props":18635,"children":18637},{"className":18636},[],[18638],{"type":27,"value":18639},"EmailAddress",{"type":27,"value":18641}," data type:",{"type":22,"tag":886,"props":18643,"children":18645},{"className":2854,"code":18644,"language":2856,"meta":8,"style":8},"class EmailAddress {\n    public readonly string value; // Exposes the validated email as a property\n    public Email(string value) { /* validates `value` via regex */ }\n}\n\n...\n\nvoid sendReminder(Appointment appointment, EmailAddress email) { ... }\n",[18646],{"type":22,"tag":486,"props":18647,"children":18648},{"__ignoreMap":8},[18649,18666,18697,18735,18742,18749,18761,18768],{"type":22,"tag":896,"props":18650,"children":18651},{"class":898,"line":899},[18652,18657,18662],{"type":22,"tag":896,"props":18653,"children":18654},{"style":1691},[18655],{"type":27,"value":18656},"class",{"type":22,"tag":896,"props":18658,"children":18659},{"style":933},[18660],{"type":27,"value":18661}," EmailAddress",{"type":22,"tag":896,"props":18663,"children":18664},{"style":903},[18665],{"type":27,"value":1626},{"type":22,"tag":896,"props":18667,"children":18668},{"class":898,"line":758},[18669,18674,18678,18682,18687,18692],{"type":22,"tag":896,"props":18670,"children":18671},{"style":1691},[18672],{"type":27,"value":18673},"    public",{"type":22,"tag":896,"props":18675,"children":18676},{"style":1691},[18677],{"type":27,"value":3953},{"type":22,"tag":896,"props":18679,"children":18680},{"style":1691},[18681],{"type":27,"value":2878},{"type":22,"tag":896,"props":18683,"children":18684},{"style":933},[18685],{"type":27,"value":18686}," value",{"type":22,"tag":896,"props":18688,"children":18689},{"style":903},[18690],{"type":27,"value":18691},"; ",{"type":22,"tag":896,"props":18693,"children":18694},{"style":2070},[18695],{"type":27,"value":18696},"// Exposes the validated email as a property\n",{"type":22,"tag":896,"props":18698,"children":18699},{"class":898,"line":755},[18700,18704,18709,18713,18717,18721,18725,18730],{"type":22,"tag":896,"props":18701,"children":18702},{"style":1691},[18703],{"type":27,"value":18673},{"type":22,"tag":896,"props":18705,"children":18706},{"style":933},[18707],{"type":27,"value":18708}," Email",{"type":22,"tag":896,"props":18710,"children":18711},{"style":903},[18712],{"type":27,"value":2888},{"type":22,"tag":896,"props":18714,"children":18715},{"style":1691},[18716],{"type":27,"value":3923},{"type":22,"tag":896,"props":18718,"children":18719},{"style":933},[18720],{"type":27,"value":18686},{"type":22,"tag":896,"props":18722,"children":18723},{"style":903},[18724],{"type":27,"value":18602},{"type":22,"tag":896,"props":18726,"children":18727},{"style":2070},[18728],{"type":27,"value":18729},"/* validates `value` via regex */",{"type":22,"tag":896,"props":18731,"children":18732},{"style":903},[18733],{"type":27,"value":18734}," }\n",{"type":22,"tag":896,"props":18736,"children":18737},{"class":898,"line":983},[18738],{"type":22,"tag":896,"props":18739,"children":18740},{"style":903},[18741],{"type":27,"value":1745},{"type":22,"tag":896,"props":18743,"children":18744},{"class":898,"line":1013},[18745],{"type":22,"tag":896,"props":18746,"children":18747},{"emptyLinePlaceholder":3481},[18748],{"type":27,"value":3484},{"type":22,"tag":896,"props":18750,"children":18751},{"class":898,"line":1030},[18752,18756],{"type":22,"tag":896,"props":18753,"children":18754},{"style":1691},[18755],{"type":27,"value":18607},{"type":22,"tag":896,"props":18757,"children":18758},{"style":903},[18759],{"type":27,"value":18760},".\n",{"type":22,"tag":896,"props":18762,"children":18763},{"class":898,"line":1068},[18764],{"type":22,"tag":896,"props":18765,"children":18766},{"emptyLinePlaceholder":3481},[18767],{"type":27,"value":3484},{"type":22,"tag":896,"props":18769,"children":18770},{"class":898,"line":1085},[18771,18775,18779,18783,18787,18791,18795,18799,18803,18807,18811],{"type":22,"tag":896,"props":18772,"children":18773},{"style":1691},[18774],{"type":27,"value":18565},{"type":22,"tag":896,"props":18776,"children":18777},{"style":933},[18778],{"type":27,"value":18570},{"type":22,"tag":896,"props":18780,"children":18781},{"style":903},[18782],{"type":27,"value":2888},{"type":22,"tag":896,"props":18784,"children":18785},{"style":933},[18786],{"type":27,"value":18579},{"type":22,"tag":896,"props":18788,"children":18789},{"style":933},[18790],{"type":27,"value":18584},{"type":22,"tag":896,"props":18792,"children":18793},{"style":903},[18794],{"type":27,"value":236},{"type":22,"tag":896,"props":18796,"children":18797},{"style":933},[18798],{"type":27,"value":18639},{"type":22,"tag":896,"props":18800,"children":18801},{"style":933},[18802],{"type":27,"value":18597},{"type":22,"tag":896,"props":18804,"children":18805},{"style":903},[18806],{"type":27,"value":18602},{"type":22,"tag":896,"props":18808,"children":18809},{"style":1691},[18810],{"type":27,"value":18607},{"type":22,"tag":896,"props":18812,"children":18813},{"style":903},[18814],{"type":27,"value":18612},{"type":22,"tag":23,"props":18816,"children":18817},{},[18818,18820,18825,18827,18832,18834,18839,18841,18847],{"type":27,"value":18819},"Now the precondition \"",{"type":22,"tag":486,"props":18821,"children":18823},{"className":18822},[],[18824],{"type":27,"value":18623},{"type":27,"value":18826}," must be a valid email address\" is encoded in the type of ",{"type":22,"tag":486,"props":18828,"children":18830},{"className":18829},[],[18831],{"type":27,"value":18548},{"type":27,"value":18833}," itself. This precondition is not formally verified, so it depends on the implementation of ",{"type":22,"tag":486,"props":18835,"children":18837},{"className":18836},[],[18838],{"type":27,"value":18639},{"type":27,"value":18840}," being bug-free, but that's an easy one to cover with a couple unit tests. And now, if there's a bug somewhere, the stack trace will be right at the source, i.e. where someone tried to call ",{"type":22,"tag":486,"props":18842,"children":18844},{"className":18843},[],[18845],{"type":27,"value":18846},"new Email()",{"type":27,"value":18848}," with something that isn't an email.",{"type":22,"tag":23,"props":18850,"children":18851},{},[18852],{"type":27,"value":18853},"Object-oriented languages unfortunately tend to require a lot of boilerplate to define data types like these, but it's still an easy way to trade a little bit more verboseness in exchange for a lot of robustness.",{"type":22,"tag":193,"props":18855,"children":18857},{"id":18856},"conclusion",[18858],{"type":27,"value":18859},"Conclusion",{"type":22,"tag":23,"props":18861,"children":18862},{},[18863,18865,18872,18873,18880],{"type":27,"value":18864},"There are many more interesting aspects of Idris to explore (like its Quantitative Type Theory which lets you express whether a variable can be used zero, one, or many times), but this has already been a lot! If you're interested in giving Idris a go, I would recommend ",{"type":22,"tag":30,"props":18866,"children":18869},{"href":18867,"rel":18868},"https://www.youtube.com/watch?v=X36ye-1x_HQ",[34],[18870],{"type":27,"value":18871},"this introductory video",{"type":27,"value":1993},{"type":22,"tag":30,"props":18874,"children":18877},{"href":18875,"rel":18876},"https://idris2.readthedocs.io/en/latest/tutorial/index.html",[34],[18878],{"type":27,"value":18879},"the official \"Crash Course\" documentation",{"type":27,"value":71},{"type":22,"tag":1999,"props":18882,"children":18883},{},[18884],{"type":27,"value":2457},{"title":8,"searchDepth":755,"depth":755,"links":18886},[18887,18888,18889,18890,18894],{"id":16861,"depth":758,"text":16864},{"id":17265,"depth":758,"text":17268},{"id":18450,"depth":758,"text":18453},{"id":18494,"depth":758,"text":18497,"children":18891},[18892,18893],{"id":18505,"depth":755,"text":18508},{"id":18516,"depth":755,"text":18519},{"id":18856,"depth":758,"text":18859},"content:phendry:2021-08-15:ExploringDependentTypesInIdris.md","phendry/2021-08-15/ExploringDependentTypesInIdris.md","phendry/2021-08-15/ExploringDependentTypesInIdris",{"user":774,"name":775},{"_path":18900,"_dir":18901,"_draft":7,"_partial":7,"_locale":8,"title":18902,"description":18903,"image":18904,"tags":18905,"publishDate":18901,"excerpt":18903,"body":18906,"_type":767,"_id":19690,"_source":769,"_file":19691,"_stem":19692,"_extension":772,"author":19693},"/phendry/2021-07-30/spotthevulndataranges","2021-07-30","Spot the Vulnerability: Data Ranges and Untrusted Input","In 1997, a flaw was discovered in how Linux and Windows handled IP fragmentation, a Denial-of-Service vulnerability which allowed systems to be crashed remotely.","/phendry/2021-07-30/img/vulnerability.jpg",[2818,2819],{"type":19,"children":18907,"toc":19683},[18908,18912,18918,18923,18928,18934,18939,18948,18953,19345,19392,19396,19406,19444,19479,19529,19534,19543,19555,19590,19596,19615,19628,19632,19679],{"type":22,"tag":23,"props":18909,"children":18910},{},[18911],{"type":27,"value":18903},{"type":22,"tag":193,"props":18913,"children":18915},{"id":18914},"background-ip-fragmentation",[18916],{"type":27,"value":18917},"Background: IP Fragmentation",{"type":22,"tag":23,"props":18919,"children":18920},{},[18921],{"type":27,"value":18922},"IP Fragmentation occurs when a packet passes through a link whose MTU is smaller than the packet size; it gets fragmented into multiple smaller packets, and subsequently reassembled by the receiver.",{"type":22,"tag":23,"props":18924,"children":18925},{},[18926],{"type":27,"value":18927},"The data in these fragments would typically be non-overlapping, but since partial overlap is permitted by the protocol, IP implementations need to handle that case.",{"type":22,"tag":193,"props":18929,"children":18931},{"id":18930},"re-assembling-fragmented-packets",[18932],{"type":27,"value":18933},"Re-assembling Fragmented Packets",{"type":22,"tag":23,"props":18935,"children":18936},{},[18937],{"type":27,"value":18938},"When Linux reassembled fragmented packets, it would loop over each fragment, copying the payload of each fragment into a newly-allocated buffer. In order to handle the case where a fragment overlaps with the previous fragment, if its start offset was less than the end offset of the previous fragment, then its start offset would be moved forward to eliminate the overlap. Here's a diagram to help visualize this:",{"type":22,"tag":886,"props":18940,"children":18943},{"className":18941,"code":18942,"language":27},[4580],"0                    22                40\nv                     v                 v\n|-----Fragment--1-----|\n                  |-----Fragment--2-----|\n                  ^  \\\n                 19   \\\n                       Overlap here\n\nbecomes...\n\n0                    22                40\nv                     v                 v\n|-----Fragment--1-----|\n                      |---Fragment--2---|\n                    \\\n                     \\\n                      Overlap discarded\n",[18944],{"type":22,"tag":486,"props":18945,"children":18946},{"__ignoreMap":8},[18947],{"type":27,"value":18942},{"type":22,"tag":23,"props":18949,"children":18950},{},[18951],{"type":27,"value":18952},"And here's how this was implemented (with only relevant code shown):",{"type":22,"tag":886,"props":18954,"children":18956},{"className":13527,"code":18955,"language":13529,"meta":8,"style":8},"/*\n *      Determine the position of this fragment.\n */\n\nend = offset + ntohs(iph->tot_len) - ihl;\n\n...\n\n/*\n *      We found where to put this one.\n *      Check for overlap with preceding fragment, and, if needed,\n *      align things so that any overlaps are eliminated.\n */\nif (prev != NULL && offset \u003C prev->end)\n{\n        i = prev->end - offset;\n        offset += i;    /* ptr into datagram */\n        ptr += i;       /* ptr into fragment data */\n}\n\n...\n\n/* Fill in the structure. */\nfp->offset = offset;\nfp->end = end;\nfp->len = end - offset;\n",[18957],{"type":22,"tag":486,"props":18958,"children":18959},{"__ignoreMap":8},[18960,18968,18976,18984,18991,19043,19050,19058,19065,19072,19080,19088,19096,19103,19150,19157,19183,19206,19227,19234,19241,19248,19255,19263,19288,19312],{"type":22,"tag":896,"props":18961,"children":18962},{"class":898,"line":899},[18963],{"type":22,"tag":896,"props":18964,"children":18965},{"style":2070},[18966],{"type":27,"value":18967},"/*\n",{"type":22,"tag":896,"props":18969,"children":18970},{"class":898,"line":758},[18971],{"type":22,"tag":896,"props":18972,"children":18973},{"style":2070},[18974],{"type":27,"value":18975}," *      Determine the position of this fragment.\n",{"type":22,"tag":896,"props":18977,"children":18978},{"class":898,"line":755},[18979],{"type":22,"tag":896,"props":18980,"children":18981},{"style":2070},[18982],{"type":27,"value":18983}," */\n",{"type":22,"tag":896,"props":18985,"children":18986},{"class":898,"line":983},[18987],{"type":22,"tag":896,"props":18988,"children":18989},{"emptyLinePlaceholder":3481},[18990],{"type":27,"value":3484},{"type":22,"tag":896,"props":18992,"children":18993},{"class":898,"line":1013},[18994,18999,19003,19007,19011,19016,19021,19025,19030,19034,19038],{"type":22,"tag":896,"props":18995,"children":18996},{"style":903},[18997],{"type":27,"value":18998},"end ",{"type":22,"tag":896,"props":19000,"children":19001},{"style":1691},[19002],{"type":27,"value":941},{"type":22,"tag":896,"props":19004,"children":19005},{"style":903},[19006],{"type":27,"value":8806},{"type":22,"tag":896,"props":19008,"children":19009},{"style":1691},[19010],{"type":27,"value":3713},{"type":22,"tag":896,"props":19012,"children":19013},{"style":933},[19014],{"type":27,"value":19015}," ntohs",{"type":22,"tag":896,"props":19017,"children":19018},{"style":903},[19019],{"type":27,"value":19020},"(iph",{"type":22,"tag":896,"props":19022,"children":19023},{"style":1691},[19024],{"type":27,"value":15573},{"type":22,"tag":896,"props":19026,"children":19027},{"style":6025},[19028],{"type":27,"value":19029},"tot_len",{"type":22,"tag":896,"props":19031,"children":19032},{"style":903},[19033],{"type":27,"value":6082},{"type":22,"tag":896,"props":19035,"children":19036},{"style":1691},[19037],{"type":27,"value":4207},{"type":22,"tag":896,"props":19039,"children":19040},{"style":903},[19041],{"type":27,"value":19042}," ihl;\n",{"type":22,"tag":896,"props":19044,"children":19045},{"class":898,"line":1030},[19046],{"type":22,"tag":896,"props":19047,"children":19048},{"emptyLinePlaceholder":3481},[19049],{"type":27,"value":3484},{"type":22,"tag":896,"props":19051,"children":19052},{"class":898,"line":1068},[19053],{"type":22,"tag":896,"props":19054,"children":19055},{"style":903},[19056],{"type":27,"value":19057},"...\n",{"type":22,"tag":896,"props":19059,"children":19060},{"class":898,"line":1085},[19061],{"type":22,"tag":896,"props":19062,"children":19063},{"emptyLinePlaceholder":3481},[19064],{"type":27,"value":3484},{"type":22,"tag":896,"props":19066,"children":19067},{"class":898,"line":1123},[19068],{"type":22,"tag":896,"props":19069,"children":19070},{"style":2070},[19071],{"type":27,"value":18967},{"type":22,"tag":896,"props":19073,"children":19074},{"class":898,"line":1161},[19075],{"type":22,"tag":896,"props":19076,"children":19077},{"style":2070},[19078],{"type":27,"value":19079}," *      We found where to put this one.\n",{"type":22,"tag":896,"props":19081,"children":19082},{"class":898,"line":2298},[19083],{"type":22,"tag":896,"props":19084,"children":19085},{"style":2070},[19086],{"type":27,"value":19087}," *      Check for overlap with preceding fragment, and, if needed,\n",{"type":22,"tag":896,"props":19089,"children":19090},{"class":898,"line":3170},[19091],{"type":22,"tag":896,"props":19092,"children":19093},{"style":2070},[19094],{"type":27,"value":19095}," *      align things so that any overlaps are eliminated.\n",{"type":22,"tag":896,"props":19097,"children":19098},{"class":898,"line":3187},[19099],{"type":22,"tag":896,"props":19100,"children":19101},{"style":2070},[19102],{"type":27,"value":18983},{"type":22,"tag":896,"props":19104,"children":19105},{"class":898,"line":3228},[19106,19110,19115,19119,19124,19128,19132,19136,19141,19145],{"type":22,"tag":896,"props":19107,"children":19108},{"style":1691},[19109],{"type":27,"value":2519},{"type":22,"tag":896,"props":19111,"children":19112},{"style":903},[19113],{"type":27,"value":19114}," (prev ",{"type":22,"tag":896,"props":19116,"children":19117},{"style":1691},[19118],{"type":27,"value":13672},{"type":22,"tag":896,"props":19120,"children":19121},{"style":1632},[19122],{"type":27,"value":19123}," NULL",{"type":22,"tag":896,"props":19125,"children":19126},{"style":1691},[19127],{"type":27,"value":2980},{"type":22,"tag":896,"props":19129,"children":19130},{"style":903},[19131],{"type":27,"value":8806},{"type":22,"tag":896,"props":19133,"children":19134},{"style":1691},[19135],{"type":27,"value":906},{"type":22,"tag":896,"props":19137,"children":19138},{"style":903},[19139],{"type":27,"value":19140}," prev",{"type":22,"tag":896,"props":19142,"children":19143},{"style":1691},[19144],{"type":27,"value":15573},{"type":22,"tag":896,"props":19146,"children":19147},{"style":903},[19148],{"type":27,"value":19149},"end)\n",{"type":22,"tag":896,"props":19151,"children":19152},{"class":898,"line":3245},[19153],{"type":22,"tag":896,"props":19154,"children":19155},{"style":903},[19156],{"type":27,"value":2571},{"type":22,"tag":896,"props":19158,"children":19159},{"class":898,"line":3286},[19160,19165,19169,19174,19178],{"type":22,"tag":896,"props":19161,"children":19162},{"style":903},[19163],{"type":27,"value":19164},"        i ",{"type":22,"tag":896,"props":19166,"children":19167},{"style":1691},[19168],{"type":27,"value":941},{"type":22,"tag":896,"props":19170,"children":19171},{"style":903},[19172],{"type":27,"value":19173}," prev->end ",{"type":22,"tag":896,"props":19175,"children":19176},{"style":1691},[19177],{"type":27,"value":4207},{"type":22,"tag":896,"props":19179,"children":19180},{"style":903},[19181],{"type":27,"value":19182}," offset;\n",{"type":22,"tag":896,"props":19184,"children":19185},{"class":898,"line":3303},[19186,19191,19196,19201],{"type":22,"tag":896,"props":19187,"children":19188},{"style":903},[19189],{"type":27,"value":19190},"        offset ",{"type":22,"tag":896,"props":19192,"children":19193},{"style":1691},[19194],{"type":27,"value":19195},"+=",{"type":22,"tag":896,"props":19197,"children":19198},{"style":903},[19199],{"type":27,"value":19200}," i;",{"type":22,"tag":896,"props":19202,"children":19203},{"style":2070},[19204],{"type":27,"value":19205},"    /* ptr into datagram */\n",{"type":22,"tag":896,"props":19207,"children":19208},{"class":898,"line":3344},[19209,19214,19218,19222],{"type":22,"tag":896,"props":19210,"children":19211},{"style":903},[19212],{"type":27,"value":19213},"        ptr ",{"type":22,"tag":896,"props":19215,"children":19216},{"style":1691},[19217],{"type":27,"value":19195},{"type":22,"tag":896,"props":19219,"children":19220},{"style":903},[19221],{"type":27,"value":19200},{"type":22,"tag":896,"props":19223,"children":19224},{"style":2070},[19225],{"type":27,"value":19226},"       /* ptr into fragment data */\n",{"type":22,"tag":896,"props":19228,"children":19229},{"class":898,"line":3361},[19230],{"type":22,"tag":896,"props":19231,"children":19232},{"style":903},[19233],{"type":27,"value":1745},{"type":22,"tag":896,"props":19235,"children":19236},{"class":898,"line":3402},[19237],{"type":22,"tag":896,"props":19238,"children":19239},{"emptyLinePlaceholder":3481},[19240],{"type":27,"value":3484},{"type":22,"tag":896,"props":19242,"children":19243},{"class":898,"line":3419},[19244],{"type":22,"tag":896,"props":19245,"children":19246},{"style":903},[19247],{"type":27,"value":19057},{"type":22,"tag":896,"props":19249,"children":19250},{"class":898,"line":3460},[19251],{"type":22,"tag":896,"props":19252,"children":19253},{"emptyLinePlaceholder":3481},[19254],{"type":27,"value":3484},{"type":22,"tag":896,"props":19256,"children":19257},{"class":898,"line":3477},[19258],{"type":22,"tag":896,"props":19259,"children":19260},{"style":2070},[19261],{"type":27,"value":19262},"/* Fill in the structure. */\n",{"type":22,"tag":896,"props":19264,"children":19265},{"class":898,"line":3487},[19266,19271,19275,19280,19284],{"type":22,"tag":896,"props":19267,"children":19268},{"style":903},[19269],{"type":27,"value":19270},"fp",{"type":22,"tag":896,"props":19272,"children":19273},{"style":1691},[19274],{"type":27,"value":15573},{"type":22,"tag":896,"props":19276,"children":19277},{"style":903},[19278],{"type":27,"value":19279},"offset ",{"type":22,"tag":896,"props":19281,"children":19282},{"style":1691},[19283],{"type":27,"value":941},{"type":22,"tag":896,"props":19285,"children":19286},{"style":903},[19287],{"type":27,"value":19182},{"type":22,"tag":896,"props":19289,"children":19290},{"class":898,"line":3505},[19291,19295,19299,19303,19307],{"type":22,"tag":896,"props":19292,"children":19293},{"style":903},[19294],{"type":27,"value":19270},{"type":22,"tag":896,"props":19296,"children":19297},{"style":1691},[19298],{"type":27,"value":15573},{"type":22,"tag":896,"props":19300,"children":19301},{"style":903},[19302],{"type":27,"value":18998},{"type":22,"tag":896,"props":19304,"children":19305},{"style":1691},[19306],{"type":27,"value":941},{"type":22,"tag":896,"props":19308,"children":19309},{"style":903},[19310],{"type":27,"value":19311}," end;\n",{"type":22,"tag":896,"props":19313,"children":19314},{"class":898,"line":5985},[19315,19319,19323,19328,19332,19337,19341],{"type":22,"tag":896,"props":19316,"children":19317},{"style":903},[19318],{"type":27,"value":19270},{"type":22,"tag":896,"props":19320,"children":19321},{"style":1691},[19322],{"type":27,"value":15573},{"type":22,"tag":896,"props":19324,"children":19325},{"style":903},[19326],{"type":27,"value":19327},"len ",{"type":22,"tag":896,"props":19329,"children":19330},{"style":1691},[19331],{"type":27,"value":941},{"type":22,"tag":896,"props":19333,"children":19334},{"style":903},[19335],{"type":27,"value":19336}," end ",{"type":22,"tag":896,"props":19338,"children":19339},{"style":1691},[19340],{"type":27,"value":4207},{"type":22,"tag":896,"props":19342,"children":19343},{"style":903},[19344],{"type":27,"value":19182},{"type":22,"tag":23,"props":19346,"children":19347},{},[19348,19354,19356,19361,19363,19368,19370,19376,19378,19383,19384,19390],{"type":22,"tag":486,"props":19349,"children":19351},{"className":19350},[],[19352],{"type":27,"value":19353},"offset",{"type":27,"value":19355}," is the fragment's offset in the complete datagram. So if ",{"type":22,"tag":486,"props":19357,"children":19359},{"className":19358},[],[19360],{"type":27,"value":19353},{"type":27,"value":19362}," is before the end of the previous fragment, then the middle ",{"type":22,"tag":486,"props":19364,"children":19366},{"className":19365},[],[19367],{"type":27,"value":2519},{"type":27,"value":19369}," block above sets it to the value of ",{"type":22,"tag":486,"props":19371,"children":19373},{"className":19372},[],[19374],{"type":27,"value":19375},"prev->end",{"type":27,"value":19377},", eliminating the overlap. Finally, length of the fragment is updated based on the new ",{"type":22,"tag":486,"props":19379,"children":19381},{"className":19380},[],[19382],{"type":27,"value":19353},{"type":27,"value":1441},{"type":22,"tag":486,"props":19385,"children":19387},{"className":19386},[],[19388],{"type":27,"value":19389},"end",{"type":27,"value":19391}," of the fragment.",{"type":22,"tag":193,"props":19393,"children":19394},{"id":15477},[19395],{"type":27,"value":15480},{"type":22,"tag":23,"props":19397,"children":19398},{},[19399,19401],{"type":27,"value":19400},"Whenever we're using arithmetic to compute a size or length, we need to ask ourselves: ",{"type":22,"tag":267,"props":19402,"children":19403},{},[19404],{"type":27,"value":19405},"can this turn out negative?",{"type":22,"tag":886,"props":19407,"children":19409},{"className":13527,"code":19408,"language":13529,"meta":8,"style":8},"fp->len = end - offset;\n",[19410],{"type":22,"tag":486,"props":19411,"children":19412},{"__ignoreMap":8},[19413],{"type":22,"tag":896,"props":19414,"children":19415},{"class":898,"line":899},[19416,19420,19424,19428,19432,19436,19440],{"type":22,"tag":896,"props":19417,"children":19418},{"style":903},[19419],{"type":27,"value":19270},{"type":22,"tag":896,"props":19421,"children":19422},{"style":1691},[19423],{"type":27,"value":15573},{"type":22,"tag":896,"props":19425,"children":19426},{"style":903},[19427],{"type":27,"value":19327},{"type":22,"tag":896,"props":19429,"children":19430},{"style":1691},[19431],{"type":27,"value":941},{"type":22,"tag":896,"props":19433,"children":19434},{"style":903},[19435],{"type":27,"value":19336},{"type":22,"tag":896,"props":19437,"children":19438},{"style":1691},[19439],{"type":27,"value":4207},{"type":22,"tag":896,"props":19441,"children":19442},{"style":903},[19443],{"type":27,"value":19182},{"type":22,"tag":23,"props":19445,"children":19446},{},[19447,19449,19454,19456,19462,19464,19469,19471,19477],{"type":27,"value":19448},"At first glance, it seems like the answer is \"no\" for this fragment: ",{"type":22,"tag":486,"props":19450,"children":19452},{"className":19451},[],[19453],{"type":27,"value":19389},{"type":27,"value":19455}," is defined by adding the fragment's payload length (definitely ",{"type":22,"tag":486,"props":19457,"children":19459},{"className":19458},[],[19460],{"type":27,"value":19461},">= 0",{"type":27,"value":19463},") to the value of ",{"type":22,"tag":486,"props":19465,"children":19467},{"className":19466},[],[19468],{"type":27,"value":19353},{"type":27,"value":19470},", so it would seem that ",{"type":22,"tag":486,"props":19472,"children":19474},{"className":19473},[],[19475],{"type":27,"value":19476},"end - offset >= 0",{"type":27,"value":19478}," must be true.",{"type":22,"tag":23,"props":19480,"children":19481},{},[19482,19484,19489,19491,19497,19499,19504,19506,19511,19513,19519,19521,19527],{"type":27,"value":19483},"However, this analysis ignores what might have happened in between the assignment of ",{"type":22,"tag":486,"props":19485,"children":19487},{"className":19486},[],[19488],{"type":27,"value":19389},{"type":27,"value":19490}," and the evaluation of ",{"type":22,"tag":486,"props":19492,"children":19494},{"className":19493},[],[19495],{"type":27,"value":19496},"end - offset",{"type":27,"value":19498},"; in particular, the handling of overlap. ",{"type":22,"tag":486,"props":19500,"children":19502},{"className":19501},[],[19503],{"type":27,"value":19353},{"type":27,"value":19505}," effectively gets set to ",{"type":22,"tag":486,"props":19507,"children":19509},{"className":19508},[],[19510],{"type":27,"value":19375},{"type":27,"value":19512}," (the end of the previous fragment), but still, this fragment should at worst be a duplicate of the previous one, meaning ",{"type":22,"tag":486,"props":19514,"children":19516},{"className":19515},[],[19517],{"type":27,"value":19518},"prev->end == end",{"type":27,"value":19520},", and consequently ",{"type":22,"tag":486,"props":19522,"children":19524},{"className":19523},[],[19525],{"type":27,"value":19526},"end - offset == 0",{"type":27,"value":19528},". So after a second look, this might still look problem-free.",{"type":22,"tag":23,"props":19530,"children":19531},{},[19532],{"type":27,"value":19533},"The dangerous word here is \"should\". Suppose a pair of fragments comes in that look more like this:",{"type":22,"tag":886,"props":19535,"children":19538},{"className":19536,"code":19537,"language":27},[4580],"0                    22\nv                     v\n|-----Fragment--1-----|\n    |--Fragment--2--|\n    ^               ^\n    4              20\n",[19539],{"type":22,"tag":486,"props":19540,"children":19541},{"__ignoreMap":8},[19542],{"type":27,"value":19537},{"type":22,"tag":23,"props":19544,"children":19545},{},[19546,19548,19553],{"type":27,"value":19547},"This is obviously wrong and shouldn't happen; fragments might overlap or duplicate on another, but they should never be a strict subset of another fragment. Maybe so, but ",{"type":22,"tag":267,"props":19549,"children":19550},{},[19551],{"type":27,"value":19552},"can they, anyway?",{"type":27,"value":19554}," It turns out the answer is yes, they can, for example if they're deliberately crafted that way by a malicious sender.",{"type":22,"tag":23,"props":19556,"children":19557},{},[19558,19560,19565,19567,19572,19574,19580,19582,19588],{"type":27,"value":19559},"If the length of the second fragment is not enough to \"cover\" the overlap correction, then the updated ",{"type":22,"tag":486,"props":19561,"children":19563},{"className":19562},[],[19564],{"type":27,"value":19353},{"type":27,"value":19566}," of the fragment becomes greater than its ",{"type":22,"tag":486,"props":19568,"children":19570},{"className":19569},[],[19571],{"type":27,"value":19389},{"type":27,"value":19573},", and consequently the computed ",{"type":22,"tag":486,"props":19575,"children":19577},{"className":19576},[],[19578],{"type":27,"value":19579},"fp->len",{"type":27,"value":19581}," is negative. This length eventually gets passed to ",{"type":22,"tag":486,"props":19583,"children":19585},{"className":19584},[],[19586],{"type":27,"value":19587},"memcpy()",{"type":27,"value":19589},", causing a buffer overflow and an extremely large copy that will lock up or crash the system.",{"type":22,"tag":193,"props":19591,"children":19593},{"id":19592},"what-went-wrong",[19594],{"type":27,"value":19595},"What Went Wrong?",{"type":22,"tag":23,"props":19597,"children":19598},{},[19599,19601,19606,19608,19613],{"type":27,"value":19600},"The root cause here was arguably a misplaced trust in untrusted input; it's a very easy mistake to make as a developer, since we naturally expect that all parts of a system ",{"type":22,"tag":267,"props":19602,"children":19603},{},[19604],{"type":27,"value":19605},"want",{"type":27,"value":19607}," to work correctly (even the parts that we don't control). In this case, input is coming from a completely untrusted source, and while there's a whole class of inputs which a robust sender should never send, there's nothing which ",{"type":22,"tag":267,"props":19609,"children":19610},{},[19611],{"type":27,"value":19612},"prevents",{"type":27,"value":19614}," a sender from doing so.",{"type":22,"tag":23,"props":19616,"children":19617},{},[19618,19620,19626],{"type":27,"value":19619},"Given that the input can contain a fragment which is a strict subset of the previous fragment, the implementation needs to guard against this possibility, for example with a ",{"type":22,"tag":486,"props":19621,"children":19623},{"className":19622},[],[19624],{"type":27,"value":19625},"if (offset > end)",{"type":27,"value":19627}," check.",{"type":22,"tag":193,"props":19629,"children":19630},{"id":16779},[19631],{"type":27,"value":16782},{"type":22,"tag":111,"props":19633,"children":19634},{},[19635,19646,19651],{"type":22,"tag":115,"props":19636,"children":19637},{},[19638,19640,19644],{"type":27,"value":19639},"Whenever untrusted data is being processed, it should be treated with the mindset of an attacker: consider not only the expected format of the input, but rather ",{"type":22,"tag":267,"props":19641,"children":19642},{},[19643],{"type":27,"value":9047},{"type":27,"value":19645}," possible formats in could take.",{"type":22,"tag":115,"props":19647,"children":19648},{},[19649],{"type":27,"value":19650},"Whenever a length or size is being computed, particularly in a memory-unsafe language like C, verify that it's not possible for the value to be negative or otherwise invalid.",{"type":22,"tag":115,"props":19652,"children":19653},{},[19654,19656],{"type":27,"value":19655},"When working with data ranges, carefully consider the edge cases, such as:\n",{"type":22,"tag":111,"props":19657,"children":19658},{},[19659,19664,19669,19674],{"type":22,"tag":115,"props":19660,"children":19661},{},[19662],{"type":27,"value":19663},"Are the ranges always ordered by their start index?",{"type":22,"tag":115,"props":19665,"children":19666},{},[19667],{"type":27,"value":19668},"Can zero-length ranges exist?",{"type":22,"tag":115,"props":19670,"children":19671},{},[19672],{"type":27,"value":19673},"Must the ranges be contiguous? (Can gaps exist between them?)",{"type":22,"tag":115,"props":19675,"children":19676},{},[19677],{"type":27,"value":19678},"Can the ranges overlap? If so, are all types of overlap handled? (Adjacent, partial overlap, duplicate, strict subset)",{"type":22,"tag":1999,"props":19680,"children":19681},{},[19682],{"type":27,"value":2457},{"title":8,"searchDepth":755,"depth":755,"links":19684},[19685,19686,19687,19688,19689],{"id":18914,"depth":758,"text":18917},{"id":18930,"depth":758,"text":18933},{"id":15477,"depth":758,"text":15480},{"id":19592,"depth":758,"text":19595},{"id":16779,"depth":758,"text":16782},"content:phendry:2021-07-30:SpotTheVulnDataRanges.md","phendry/2021-07-30/SpotTheVulnDataRanges.md","phendry/2021-07-30/SpotTheVulnDataRanges",{"user":774,"name":775},{"_path":19695,"_dir":19696,"_draft":7,"_partial":7,"_locale":8,"title":19697,"description":19698,"tags":19699,"publishDate":19701,"image":19702,"excerpt":19698,"body":19703,"_type":767,"_id":20465,"_source":769,"_file":20466,"_stem":20467,"_extension":772,"author":20468},"/phendry/2021-06/smoothupgradestovue3","2021-06","Smooth Upgrades to Vue 3","This post assumes basic familiarity with Vue.js v2.x.",[2507,15,19700],"how-to","2021-07-01","/phendry/2021-06/img/vue-transition.jpg",{"type":19,"children":19704,"toc":20459},[19705,19720,19725,19863,19877,19891,19897,19908,19979,19991,19997,20002,20053,20058,20064,20078,20083,20175,20189,20252,20265,20332,20338,20343,20433,20455],{"type":22,"tag":23,"props":19706,"children":19707},{},[19708],{"type":22,"tag":267,"props":19709,"children":19710},{},[19711,19713,19719],{"type":27,"value":19712},"This post assumes basic familiarity with ",{"type":22,"tag":30,"props":19714,"children":19716},{"href":32,"rel":19715},[34],[19717],{"type":27,"value":19718},"Vue.js v2.x",{"type":27,"value":71},{"type":22,"tag":23,"props":19721,"children":19722},{},[19723],{"type":27,"value":19724},"Vue 3, the Progressive JavaScript Framework, introduces some compelling new features:",{"type":22,"tag":111,"props":19726,"children":19727},{},[19728,19741,19764,19786,19798,19810,19828,19850],{"type":22,"tag":115,"props":19729,"children":19730},{},[19731,19732,19739],{"type":27,"value":10237},{"type":22,"tag":30,"props":19733,"children":19736},{"href":19734,"rel":19735},"https://v3.vuejs.org/guide/composition-api-introduction.html",[34],[19737],{"type":27,"value":19738},"Composition API",{"type":27,"value":19740}," introduces a new, more flexible, TypeScript-friendly syntax for defining components (albeit at the cost of a bit more complexity);",{"type":22,"tag":115,"props":19742,"children":19743},{},[19744,19746,19753,19755,19762],{"type":27,"value":19745},"Significant ",{"type":22,"tag":30,"props":19747,"children":19750},{"href":19748,"rel":19749},"https://docs.google.com/spreadsheets/d/1VJFx-kQ4KjJmnpDXIEaig-cVAAJtpIGLZNbv3Lr4CR0/edit#gid=0",[34],[19751],{"type":27,"value":19752},"reported performance improvements",{"type":27,"value":19754}," over v2, in addition to reduced bundle sizes as a result of ",{"type":22,"tag":30,"props":19756,"children":19759},{"href":19757,"rel":19758},"https://v3.vuejs.org/guide/migration/global-api-treeshaking.html",[34],[19760],{"type":27,"value":19761},"global API tree-shaking",{"type":27,"value":19763},";",{"type":22,"tag":115,"props":19765,"children":19766},{},[19767,19769,19776,19778,19784],{"type":27,"value":19768},"An updated ",{"type":22,"tag":30,"props":19770,"children":19773},{"href":19771,"rel":19772},"https://v3.vuejs.org/guide/reactivity.html#what-is-reactivity",[34],[19774],{"type":27,"value":19775},"reactivity system",{"type":27,"value":19777}," based on ",{"type":22,"tag":486,"props":19779,"children":19781},{"className":19780},[],[19782],{"type":27,"value":19783},"Proxy",{"type":27,"value":19785},", which eliminates a common source of bugs when using arrays and objects;",{"type":22,"tag":115,"props":19787,"children":19788},{},[19789,19796],{"type":22,"tag":30,"props":19790,"children":19793},{"href":19791,"rel":19792},"https://vitejs.dev/",[34],[19794],{"type":27,"value":19795},"Vite",{"type":27,"value":19797},", a new set of frontend tooling for a lightning-fast development experience;",{"type":22,"tag":115,"props":19799,"children":19800},{},[19801,19808],{"type":22,"tag":30,"props":19802,"children":19805},{"href":19803,"rel":19804},"https://v3.vuejs.org/guide/migration/fragments.html",[34],[19806],{"type":27,"value":19807},"Fragments",{"type":27,"value":19809},", allowing multiple root elements in Vue components;",{"type":22,"tag":115,"props":19811,"children":19812},{},[19813,19815,19826],{"type":27,"value":19814},"[Experimental] The ",{"type":22,"tag":30,"props":19816,"children":19819},{"href":19817,"rel":19818},"https://github.com/vuejs/rfcs/blob/script-setup-2/active-rfcs/0000-script-setup.md",[34],[19820],{"type":22,"tag":486,"props":19821,"children":19823},{"className":19822},[],[19824],{"type":27,"value":19825},"\u003Cscript setup>",{"type":27,"value":19827}," syntax, for a nicer syntax when committing fully to the Composition API in a component;",{"type":22,"tag":115,"props":19829,"children":19830},{},[19831,19833,19840,19842,19848],{"type":27,"value":19832},"[Experimental] ",{"type":22,"tag":30,"props":19834,"children":19837},{"href":19835,"rel":19836},"https://github.com/vuejs/rfcs/blob/style-vars-2/active-rfcs/0000-sfc-style-variables.md",[34],[19838],{"type":27,"value":19839},"State-driven CSS Variables",{"type":27,"value":19841},", allowing data bindings within the ",{"type":22,"tag":486,"props":19843,"children":19845},{"className":19844},[],[19846],{"type":27,"value":19847},"\u003Cstyle>",{"type":27,"value":19849}," section of components;",{"type":22,"tag":115,"props":19851,"children":19852},{},[19853,19854,19861],{"type":27,"value":19814},{"type":22,"tag":30,"props":19855,"children":19858},{"href":19856,"rel":19857},"https://v3.vuejs.org/guide/migration/suspense.html",[34],[19859],{"type":27,"value":19860},"Suspense",{"type":27,"value":19862}," component, which makes it much easier to write asynchronous components that display a \"loading\" UI while they're processing.",{"type":22,"tag":23,"props":19864,"children":19865},{},[19866,19868,19875],{"type":27,"value":19867},"It's a valuable upgrade in terms of performance and developer productivity. The question is, how do you get there? While v3 does not make any drastic API changes compared to v2, the changes it does make are ",{"type":22,"tag":30,"props":19869,"children":19872},{"href":19870,"rel":19871},"https://v3.vuejs.org/guide/migration/introduction.html#breaking-changes",[34],[19873],{"type":27,"value":19874},"numerous",{"type":27,"value":19876},", and as of the initial v3.0 release, these breaking changes would need to be migrated in one fell swoop.",{"type":22,"tag":23,"props":19878,"children":19879},{},[19880,19882,19889],{"type":27,"value":19881},"Fortunately, the Vue.js team has recently released the ",{"type":22,"tag":30,"props":19883,"children":19886},{"href":19884,"rel":19885},"https://v3.vuejs.org/guide/migration/migration-build.html",[34],[19887],{"type":27,"value":19888},"Migration Build",{"type":27,"value":19890},", which makes it possible (and easy) to make a smooth transition from v2 to v3.",{"type":22,"tag":193,"props":19892,"children":19894},{"id":19893},"vue-3-compatibility-caveats",[19895],{"type":27,"value":19896},"Vue 3 Compatibility Caveats",{"type":22,"tag":23,"props":19898,"children":19899},{},[19900,19902,19906],{"type":27,"value":19901},"There are a few reasons why you might ",{"type":22,"tag":267,"props":19903,"children":19904},{},[19905],{"type":27,"value":869},{"type":27,"value":19907}," be ready to introduce Vue 3 into your project:",{"type":22,"tag":111,"props":19909,"children":19910},{},[19911,19930],{"type":22,"tag":115,"props":19912,"children":19913},{},[19914,19919,19921,19928],{"type":22,"tag":2598,"props":19915,"children":19916},{},[19917],{"type":27,"value":19918},"IE11 Compatibility:",{"type":27,"value":19920}," Support for Internet Exporer 11 and other \"legacy\" browsers was previously promised in a subsequent update, but it has been ",{"type":22,"tag":30,"props":19922,"children":19925},{"href":19923,"rel":19924},"https://github.com/vuejs/rfcs/blob/master/active-rfcs/0038-vue3-ie11-support.md",[34],[19926],{"type":27,"value":19927},"formally dropped",{"type":27,"value":19929}," from the Vue 3 roadmap. If your project needs to support IE or other \"legacy\" browsers, you'll likely be sticking to Vue v2.x indefinitely.",{"type":22,"tag":115,"props":19931,"children":19932},{},[19933,19938,19940],{"type":22,"tag":2598,"props":19934,"children":19935},{},[19936],{"type":27,"value":19937},"Framework Compatibiilty:",{"type":27,"value":19939}," Many major component libraries and frameworks need time to make the upgrade. If you depend on one of them, you'll be dependent on their upgrade to v3. For example:\n",{"type":22,"tag":111,"props":19941,"children":19942},{},[19943,19955,19967],{"type":22,"tag":115,"props":19944,"children":19945},{},[19946,19948],{"type":27,"value":19947},"Bootstrap-Vue: ",{"type":22,"tag":30,"props":19949,"children":19952},{"href":19950,"rel":19951},"https://github.com/bootstrap-vue/bootstrap-vue/issues/5196",[34],[19953],{"type":27,"value":19954},"On roadmap, but no timeline",{"type":22,"tag":115,"props":19956,"children":19957},{},[19958,19960],{"type":27,"value":19959},"Nuxt.js: ",{"type":22,"tag":30,"props":19961,"children":19964},{"href":19962,"rel":19963},"https://github.com/nuxt/nuxt.js/issues/5708",[34],[19965],{"type":27,"value":19966},"On roadmap for Nuxt v3, but no timeline",{"type":22,"tag":115,"props":19968,"children":19969},{},[19970,19972],{"type":27,"value":19971},"Vuetify: ",{"type":22,"tag":30,"props":19973,"children":19976},{"href":19974,"rel":19975},"https://vuetifyjs.com/en/introduction/roadmap/",[34],[19977],{"type":27,"value":19978},"support available in Vuetify v3.0 alpha; full support in Q3 2021",{"type":22,"tag":23,"props":19980,"children":19981},{},[19982,19984,19990],{"type":27,"value":19983},"Make sure that your project's dependencies and supported platforms allow for the upgrade. If not, keep in mind that some v3 features are being backported to v2, such as the ",{"type":22,"tag":30,"props":19985,"children":19988},{"href":19986,"rel":19987},"https://github.com/vuejs/composition-api",[34],[19989],{"type":27,"value":19738},{"type":27,"value":71},{"type":22,"tag":193,"props":19992,"children":19994},{"id":19993},"what-is-the-migration-build",[19995],{"type":27,"value":19996},"What is the Migration Build?",{"type":22,"tag":23,"props":19998,"children":19999},{},[20000],{"type":27,"value":20001},"The Migration Build is an alternative build of Vue 3 which provides configurable Vue 2 compatible behaviour. It enables a gradual migration process:",{"type":22,"tag":847,"props":20003,"children":20004},{},[20005,20024,20029,20034],{"type":22,"tag":115,"props":20006,"children":20007},{},[20008,20010,20015,20017,20023],{"type":27,"value":20009},"Swap out your ",{"type":22,"tag":486,"props":20011,"children":20013},{"className":20012},[],[20014],{"type":27,"value":15},{"type":27,"value":20016}," v2.x dependency with ",{"type":22,"tag":486,"props":20018,"children":20020},{"className":20019},[],[20021],{"type":27,"value":20022},"@vue/compat",{"type":27,"value":19763},{"type":22,"tag":115,"props":20025,"children":20026},{},[20027],{"type":27,"value":20028},"Configure the Migration Build to be fully v2-compatible;",{"type":22,"tag":115,"props":20030,"children":20031},{},[20032],{"type":27,"value":20033},"One breaking change at a time, configure the Migration Build to be v3-compatible for that feature, and fix any occurrences of it in your project;",{"type":22,"tag":115,"props":20035,"children":20036},{},[20037,20039,20044,20046,20051],{"type":27,"value":20038},"Swap out ",{"type":22,"tag":486,"props":20040,"children":20042},{"className":20041},[],[20043],{"type":27,"value":20022},{"type":27,"value":20045}," for ",{"type":22,"tag":486,"props":20047,"children":20049},{"className":20048},[],[20050],{"type":27,"value":15},{"type":27,"value":20052}," 3.x once your project is fully migrated to v3.",{"type":22,"tag":23,"props":20054,"children":20055},{},[20056],{"type":27,"value":20057},"The amount of overhead introduced by this compability build is relatively small, so it's even possible to make releases while in the intermediate state between v2 and v3 compatibility. The Vue.js team has guaranteed to maintain this migration build at least until the end of 2021, but after that no firm commitments have been made, so if you've made the decision to upgrade, it's best not to delay for too long.",{"type":22,"tag":193,"props":20059,"children":20061},{"id":20060},"using-the-migration-build",[20062],{"type":27,"value":20063},"Using the Migration Build",{"type":22,"tag":23,"props":20065,"children":20066},{},[20067,20069,20076],{"type":27,"value":20068},"The first step of course is installation, and the ",{"type":22,"tag":30,"props":20070,"children":20073},{"href":20071,"rel":20072},"https://v3.vuejs.org/guide/migration/migration-build.html#installation",[34],[20074],{"type":27,"value":20075},"official installation documentation",{"type":27,"value":20077}," is easy to follow.",{"type":22,"tag":23,"props":20079,"children":20080},{},[20081],{"type":27,"value":20082},"Configuration is straightforward, and can be done both globally or on a per-component basis. A global configuration can be created with a snippet like this in the root of the application:",{"type":22,"tag":886,"props":20084,"children":20086},{"className":2505,"code":20085,"language":2507,"meta":8,"style":8},"import { configureCompat } from 'vue';\n\nconfigureCompat({\n   // By default, each feature is v2 compatible. Set individual features to\n   // `false` like so to switch them to v3 compatibility.\n   GLOBAL_MOUNT: false,\n});\n",[20087],{"type":22,"tag":486,"props":20088,"children":20089},{"__ignoreMap":8},[20090,20117,20124,20136,20144,20152,20168],{"type":22,"tag":896,"props":20091,"children":20092},{"class":898,"line":899},[20093,20098,20103,20108,20113],{"type":22,"tag":896,"props":20094,"children":20095},{"style":1691},[20096],{"type":27,"value":20097},"import",{"type":22,"tag":896,"props":20099,"children":20100},{"style":903},[20101],{"type":27,"value":20102}," { configureCompat } ",{"type":22,"tag":896,"props":20104,"children":20105},{"style":1691},[20106],{"type":27,"value":20107},"from",{"type":22,"tag":896,"props":20109,"children":20110},{"style":944},[20111],{"type":27,"value":20112}," 'vue'",{"type":22,"tag":896,"props":20114,"children":20115},{"style":903},[20116],{"type":27,"value":1649},{"type":22,"tag":896,"props":20118,"children":20119},{"class":898,"line":758},[20120],{"type":22,"tag":896,"props":20121,"children":20122},{"emptyLinePlaceholder":3481},[20123],{"type":27,"value":3484},{"type":22,"tag":896,"props":20125,"children":20126},{"class":898,"line":755},[20127,20132],{"type":22,"tag":896,"props":20128,"children":20129},{"style":933},[20130],{"type":27,"value":20131},"configureCompat",{"type":22,"tag":896,"props":20133,"children":20134},{"style":903},[20135],{"type":27,"value":8574},{"type":22,"tag":896,"props":20137,"children":20138},{"class":898,"line":983},[20139],{"type":22,"tag":896,"props":20140,"children":20141},{"style":2070},[20142],{"type":27,"value":20143},"   // By default, each feature is v2 compatible. Set individual features to\n",{"type":22,"tag":896,"props":20145,"children":20146},{"class":898,"line":1013},[20147],{"type":22,"tag":896,"props":20148,"children":20149},{"style":2070},[20150],{"type":27,"value":20151},"   // `false` like so to switch them to v3 compatibility.\n",{"type":22,"tag":896,"props":20153,"children":20154},{"class":898,"line":1030},[20155,20160,20164],{"type":22,"tag":896,"props":20156,"children":20157},{"style":903},[20158],{"type":27,"value":20159},"   GLOBAL_MOUNT: ",{"type":22,"tag":896,"props":20161,"children":20162},{"style":1632},[20163],{"type":27,"value":5017},{"type":22,"tag":896,"props":20165,"children":20166},{"style":903},[20167],{"type":27,"value":3988},{"type":22,"tag":896,"props":20169,"children":20170},{"class":898,"line":1068},[20171],{"type":22,"tag":896,"props":20172,"children":20173},{"style":903},[20174],{"type":27,"value":5450},{"type":22,"tag":23,"props":20176,"children":20177},{},[20178,20180,20187],{"type":27,"value":20179},"Once installed and configured, your v3-upgraded application should, for the most part, function just the same as before. From here, the ",{"type":22,"tag":30,"props":20181,"children":20184},{"href":20182,"rel":20183},"https://v3.vuejs.org/guide/migration/migration-build.html#feature-reference",[34],[20185],{"type":27,"value":20186},"Feature Reference",{"type":27,"value":20188}," in the official documentation is where you'll find a list of the individual v3 features to enable in order to migrate your project. They're separated into four \"Compatibility Types\", in a sensible order for sequentially working through them. These are:",{"type":22,"tag":111,"props":20190,"children":20191},{},[20192,20208,20218,20242],{"type":22,"tag":115,"props":20193,"children":20194},{},[20195,20200,20202,20206],{"type":22,"tag":2598,"props":20196,"children":20197},{},[20198],{"type":27,"value":20199},"Incompatible:",{"type":27,"value":20201}," These are a handful of features for which the Migration Build does ",{"type":22,"tag":267,"props":20203,"children":20204},{},[20205],{"type":27,"value":869},{"type":27,"value":20207}," provide v2 compatibility; it only provides warnings for easy identification of code that requires migrating. If any of these warnings appear, they'll need to be addressed upfront.",{"type":22,"tag":115,"props":20209,"children":20210},{},[20211,20216],{"type":22,"tag":2598,"props":20212,"children":20213},{},[20214],{"type":27,"value":20215},"Partially Compatible With Caveats:",{"type":27,"value":20217}," These features don't behave 100% identically to v2 in compatibility mode, but as long as you aren't relying on private Vue APIs, this is unlikely to be a problem.",{"type":22,"tag":115,"props":20219,"children":20220},{},[20221,20226,20228,20232,20234,20240],{"type":22,"tag":2598,"props":20222,"children":20223},{},[20224],{"type":27,"value":20225},"Compat Only (No Warning)",{"type":27,"value":20227},": These features are v2-compatible but do ",{"type":22,"tag":267,"props":20229,"children":20230},{},[20231],{"type":27,"value":869},{"type":27,"value":20233}," issue a warning to identify code which needs migrating. Curently this is only the ",{"type":22,"tag":486,"props":20235,"children":20237},{"className":20236},[],[20238],{"type":27,"value":20239},"TRANSITION_CLASSES",{"type":27,"value":20241}," feature, instances of which are easy to find via text search.",{"type":22,"tag":115,"props":20243,"children":20244},{},[20245,20250],{"type":22,"tag":2598,"props":20246,"children":20247},{},[20248],{"type":27,"value":20249},"Fully Compatible",{"type":27,"value":20251},": These features ought to behave identically to v2.",{"type":22,"tag":23,"props":20253,"children":20254},{},[20255,20257,20263],{"type":27,"value":20256},"For each feature, simply migrate each v2-style occurrence, then set ",{"type":22,"tag":486,"props":20258,"children":20260},{"className":20259},[],[20261],{"type":27,"value":20262},"FEATURE_NAME: false",{"type":27,"value":20264}," in your compatibility configuration in order to switch the Vue build to use the v3 behavior.",{"type":22,"tag":23,"props":20266,"children":20267},{},[20268,20270,20276,20278,20285,20287,20293,20295,20300,20301,20307,20309,20315,20316,20322,20324,20330],{"type":27,"value":20269},"For example, consider the ",{"type":22,"tag":486,"props":20271,"children":20273},{"className":20272},[],[20274],{"type":27,"value":20275},"INSTANCE_SCOPED_SLOTS",{"type":27,"value":20277}," compatibility feature. The Feature Reference provides a ",{"type":22,"tag":30,"props":20279,"children":20282},{"href":20280,"rel":20281},"https://v3.vuejs.org/guide/migration/slots-unification.html",[34],[20283],{"type":27,"value":20284},"link",{"type":27,"value":20286}," to the relevant migration documentation, which tells you that ",{"type":22,"tag":486,"props":20288,"children":20290},{"className":20289},[],[20291],{"type":27,"value":20292},"this.$scopedSlots",{"type":27,"value":20294}," has been removed due to the unification of normal/scoped slots in v3. The \"Migration Strategy\" section at the bottom describes simple steps to take: replace occurrences of ",{"type":22,"tag":486,"props":20296,"children":20298},{"className":20297},[],[20299],{"type":27,"value":20292},{"type":27,"value":13417},{"type":22,"tag":486,"props":20302,"children":20304},{"className":20303},[],[20305],{"type":27,"value":20306},"this.$slots",{"type":27,"value":20308},", and then replace occurrences of ",{"type":22,"tag":486,"props":20310,"children":20312},{"className":20311},[],[20313],{"type":27,"value":20314},"this.$slots.mySlot",{"type":27,"value":13417},{"type":22,"tag":486,"props":20317,"children":20319},{"className":20318},[],[20320],{"type":27,"value":20321},"this.$slots.mySlot()",{"type":27,"value":20323},". This can easily be done in a few moments using find-and-replace, after which ",{"type":22,"tag":486,"props":20325,"children":20327},{"className":20326},[],[20328],{"type":27,"value":20329},"INSTANCE_SCOPED_SLOTS: false",{"type":27,"value":20331}," can be set in your compatibility configuration, one step closer to v3 compatibility.",{"type":22,"tag":193,"props":20333,"children":20335},{"id":20334},"recap",[20336],{"type":27,"value":20337},"Recap",{"type":22,"tag":23,"props":20339,"children":20340},{},[20341],{"type":27,"value":20342},"Overall, the Migration Build allows for a gradual development process of migration that looks something like this:",{"type":22,"tag":847,"props":20344,"children":20345},{},[20346,20374,20379],{"type":22,"tag":115,"props":20347,"children":20348},{},[20349,20351],{"type":27,"value":20350},"Replace your project's Vue 2.x dependency with the Vue 3 Migration Build\n",{"type":22,"tag":847,"props":20352,"children":20353},{},[20354,20359,20364,20369],{"type":22,"tag":115,"props":20355,"children":20356},{},[20357],{"type":27,"value":20358},"Install and configure it for full v2 compatibility",{"type":22,"tag":115,"props":20360,"children":20361},{},[20362],{"type":27,"value":20363},"Migrate the handful of features marked \"Incompatible\" in order to restore your application to full function",{"type":22,"tag":115,"props":20365,"children":20366},{},[20367],{"type":27,"value":20368},"Peform some smoke testing to ensure that everything is back to normal",{"type":22,"tag":115,"props":20370,"children":20371},{},[20372],{"type":27,"value":20373},"Merge this change into your mainline development branch",{"type":22,"tag":115,"props":20375,"children":20376},{},[20377],{"type":27,"value":20378},"Continue regular development as required, potentially even making releases along the way",{"type":22,"tag":115,"props":20380,"children":20381},{},[20382,20384,20390,20391],{"type":27,"value":20383},"While ",{"type":22,"tag":486,"props":20385,"children":20387},{"className":20386},[],[20388],{"type":27,"value":20389},"numUnmigratedFeatures > 0",{"type":27,"value":3988},{"type":22,"tag":847,"props":20392,"children":20393},{},[20394,20406,20418,20423,20428],{"type":22,"tag":115,"props":20395,"children":20396},{},[20397,20399,20404],{"type":27,"value":20398},"Use the ",{"type":22,"tag":30,"props":20400,"children":20402},{"href":20182,"rel":20401},[34],[20403],{"type":27,"value":20186},{"type":27,"value":20405}," documentation to identify the next compatibility feature to disable",{"type":22,"tag":115,"props":20407,"children":20408},{},[20409,20411,20416],{"type":27,"value":20410},"Switch to v3 behavior by setting ",{"type":22,"tag":486,"props":20412,"children":20414},{"className":20413},[],[20415],{"type":27,"value":20262},{"type":27,"value":20417}," in your Migration Build configuration",{"type":22,"tag":115,"props":20419,"children":20420},{},[20421],{"type":27,"value":20422},"Use the documentation link in the Feature Reference to jump to relevant information about how to migrate to v3",{"type":22,"tag":115,"props":20424,"children":20425},{},[20426],{"type":27,"value":20427},"Migrate all occurrences in your project",{"type":22,"tag":115,"props":20429,"children":20430},{},[20431],{"type":27,"value":20432},"Merge this change into your mainline development branch (which ensures that any subsequent commits must use the v3 behavior)",{"type":22,"tag":23,"props":20434,"children":20435},{},[20436,20438,20445,20447,20454],{"type":27,"value":20437},"This easy and comprehensive upgrade process, combined with how infrequently Vue makes breaking API changes to begin with, arguably makes it best-in-class among JavaScript UI framworks in terms of long-term maintenance (although React is ",{"type":22,"tag":30,"props":20439,"children":20442},{"href":20440,"rel":20441},"https://reactjs.org/blog/2020/10/20/react-v17.html#gradual-upgrades",[34],[20443],{"type":27,"value":20444},"introducing a similar upgrade process",{"type":27,"value":20446}," for the upcoming version 18). It's one of the reasons ",{"type":22,"tag":30,"props":20448,"children":20451},{"href":20449,"rel":20450},"https://artandlogic.com/2020/02/why-vue/",[34],[20452],{"type":27,"value":20453},"why we recommend Vue",{"type":27,"value":71},{"type":22,"tag":1999,"props":20456,"children":20457},{},[20458],{"type":27,"value":2457},{"title":8,"searchDepth":755,"depth":755,"links":20460},[20461,20462,20463,20464],{"id":19893,"depth":758,"text":19896},{"id":19993,"depth":758,"text":19996},{"id":20060,"depth":758,"text":20063},{"id":20334,"depth":758,"text":20337},"content:phendry:2021-06:SmoothUpgradesToVue3.md","phendry/2021-06/SmoothUpgradesToVue3.md","phendry/2021-06/SmoothUpgradesToVue3",{"user":774,"name":775},{"_path":20470,"_dir":20471,"_draft":7,"_partial":7,"_locale":8,"title":20472,"description":20473,"tags":20474,"publishDate":20478,"image":20479,"excerpt":20473,"body":20480,"_type":767,"_id":21478,"_source":769,"_file":21479,"_stem":21480,"_extension":772,"author":21481},"/phendry/2019-3/restfromthebottomup","2019-3","REST from the Bottom Up","The RESTful API has a funny place in the software development world: it's widely regarded as the best general-purpose pattern for building web application APIs, and yet it's also nebulous enough of a concept to cause endless disagreements within teams over exactly how to implement one.",[20475,8038,20476,20477],"rest","web","architecture","2019-10-01","/phendry/2019-3/img/feature_image.png",{"type":19,"children":20481,"toc":21458},[20482,20486,20529,20534,20545,20551,20563,20581,20586,20591,20603,20608,20613,20618,20623,20628,20634,20639,20644,20799,20804,20810,20815,20828,20840,20845,20851,20856,20861,21009,21027,21035,21065,21070,21076,21081,21096,21110,21115,21121,21126,21167,21172,21178,21183,21189,21194,21199,21204,21212,21217,21222,21227,21233,21238,21243,21248,21255,21285,21318,21323,21329,21334,21339,21362,21368,21373,21401,21406,21414,21420,21425,21429,21434,21453],{"type":22,"tag":23,"props":20483,"children":20484},{},[20485],{"type":27,"value":20473},{"type":22,"tag":23,"props":20487,"children":20488},{},[20489,20491,20497,20498,20504,20506,20512,20514,20520,20522,20527],{"type":27,"value":20490},"Do I make my endpoint ",{"type":22,"tag":486,"props":20492,"children":20494},{"className":20493},[],[20495],{"type":27,"value":20496},"/company/123/",{"type":27,"value":2684},{"type":22,"tag":486,"props":20499,"children":20501},{"className":20500},[],[20502],{"type":27,"value":20503},"/companies/123/",{"type":27,"value":20505},"? How about ",{"type":22,"tag":486,"props":20507,"children":20509},{"className":20508},[],[20510],{"type":27,"value":20511},"/companies/123/locations/",{"type":27,"value":20513}," vs ",{"type":22,"tag":486,"props":20515,"children":20517},{"className":20516},[],[20518],{"type":27,"value":20519},"/locations/?company=123",{"type":27,"value":20521}," ? How do I handle versioning the API? Why ",{"type":22,"tag":267,"props":20523,"children":20524},{},[20525],{"type":27,"value":20526},"shouldn't",{"type":27,"value":20528}," I send a POST request to trigger an action on the server? If a backend task can take many seconds to process, how do I represent that in the API?",{"type":22,"tag":23,"props":20530,"children":20531},{},[20532],{"type":27,"value":20533},"Questions like these are asked and answered (in different ways) all over the Internet, and it can be frustrating to make sense of it all. In search of a better understanding of REST, I decided to go straight to the source: Roy Fielding's year 2000 dissertation defining REST. What I found was not exactly what I was expecting, and yet I came away with a surprising amount of practical insights to apply to my API designs.",{"type":22,"tag":23,"props":20535,"children":20536},{},[20537,20539],{"type":27,"value":20538},"We're going to walk through this paper chapter by chapter to see what there is to learn. Here's a link to it in full if you'd like to follow along: ",{"type":22,"tag":30,"props":20540,"children":20543},{"href":20541,"rel":20542},"https://www.ics.uci.edu/~fielding/pubs/dissertation/fielding_dissertation.pdf",[34],[20544],{"type":27,"value":20541},{"type":22,"tag":193,"props":20546,"children":20548},{"id":20547},"chapter-1-software-architecture",[20549],{"type":27,"value":20550},"Chapter 1: Software Architecture",{"type":22,"tag":23,"props":20552,"children":20553},{},[20554,20556,20561],{"type":27,"value":20555},"The opening chapter goes to great lengths to define \"architectural style\" and related terms. While it makes for dry reading, it's important, since there are subtle distinctions to make clear. In particular, what we would informally call \"software architecture\" is given the specific term \"architectural style\" here, and the term \"software architecture\" is reserved to describe a ",{"type":22,"tag":267,"props":20557,"children":20558},{},[20559],{"type":27,"value":20560},"specific",{"type":27,"value":20562}," implementation of an architectural style.",{"type":22,"tag":23,"props":20564,"children":20565},{},[20566,20568,20573,20575,20579],{"type":27,"value":20567},"The main motivation for this is to keep discussions of architecture well-grounded in the actual operation of the software, particularly with regards to data. \"Box and line\" architecture diagrams ignore data entirely, and so they don't properly capture the behaviour of software. When we think of \"software architecture\" as an abstraction of the ",{"type":22,"tag":267,"props":20569,"children":20570},{},[20571],{"type":27,"value":20572},"run-time",{"type":27,"value":20574}," elements of a software system, this makes explicit that different phases of a program's operation (initialization, processing, shutdown, etc) might have different architectures, with yet another architecture responsible for transitioning between these phases. An architecture ",{"type":22,"tag":267,"props":20576,"children":20577},{},[20578],{"type":27,"value":1999},{"type":27,"value":20580},", then, is a higher-level description of a \"class\" of software architectures.",{"type":22,"tag":23,"props":20582,"children":20583},{},[20584],{"type":27,"value":20585},"For reference, here are all of these terms and their definitions:",{"type":22,"tag":23,"props":20587,"children":20588},{},[20589],{"type":27,"value":20590},"software architecture : An abstraction of the run-time elements of a software system during some phase of its operation. A system may be composed of many levels of abstraction and many phases of operation, each with its own software architecture.",{"type":22,"tag":23,"props":20592,"children":20593},{},[20594,20596,20601],{"type":27,"value":20595},"element : A software architecture is defined by a configuration of architectural ",{"type":22,"tag":2598,"props":20597,"children":20598},{},[20599],{"type":27,"value":20600},"elements",{"type":27,"value":20602},"—components, connectors, and data—constrained in their relationships in order to achieve a desired set of architectural properties.",{"type":22,"tag":23,"props":20604,"children":20605},{},[20606],{"type":27,"value":20607},"component : An abstract unit of software instructions and internal state that provides a transformation of data via its interface.",{"type":22,"tag":23,"props":20609,"children":20610},{},[20611],{"type":27,"value":20612},"connector : An abstract mechanism that mediates communication, coordination, or cooperation among components.",{"type":22,"tag":23,"props":20614,"children":20615},{},[20616],{"type":27,"value":20617},"datum : An element of information that is transferred from a component, or received by a component, via a connector.",{"type":22,"tag":23,"props":20619,"children":20620},{},[20621],{"type":27,"value":20622},"configuration : The structure of architectural relationships among components, connectors, and data during a period of system run-time.",{"type":22,"tag":23,"props":20624,"children":20625},{},[20626],{"type":27,"value":20627},"architectural style : A coordinated set of architectural constraints that restricts the roles/features of architectural elements and the allowed relationships among those elements within any architecture that conforms to that style.",{"type":22,"tag":193,"props":20629,"children":20631},{"id":20630},"chapter-2-network-based-application-architectures",[20632],{"type":27,"value":20633},"Chapter 2: Network-based Application Architectures",{"type":22,"tag":23,"props":20635,"children":20636},{},[20637],{"type":27,"value":20638},"Next, Fielding defines a list of properties of architectural styles that are valuable or interesting, so that later we can evaluate a whole host of architectural styles against these properties.",{"type":22,"tag":23,"props":20640,"children":20641},{},[20642],{"type":27,"value":20643},"The properties considered are:",{"type":22,"tag":111,"props":20645,"children":20646},{},[20647,20688,20698,20708,20769,20779,20789],{"type":22,"tag":115,"props":20648,"children":20649},{},[20650,20655],{"type":22,"tag":2598,"props":20651,"children":20652},{},[20653],{"type":27,"value":20654},"Performance:",{"type":22,"tag":111,"props":20656,"children":20657},{},[20658,20668,20678],{"type":22,"tag":115,"props":20659,"children":20660},{},[20661,20666],{"type":22,"tag":2598,"props":20662,"children":20663},{},[20664],{"type":27,"value":20665},"Network performance:",{"type":27,"value":20667}," Does it send a lot of data over the network?",{"type":22,"tag":115,"props":20669,"children":20670},{},[20671,20676],{"type":22,"tag":2598,"props":20672,"children":20673},{},[20674],{"type":27,"value":20675},"User-perceived performance:",{"type":27,"value":20677}," Does it have low latency? Does it render quickly?",{"type":22,"tag":115,"props":20679,"children":20680},{},[20681,20686],{"type":22,"tag":2598,"props":20682,"children":20683},{},[20684],{"type":27,"value":20685},"Network efficiency:",{"type":27,"value":20687}," Can it avoid using the network, e.g. via caching?",{"type":22,"tag":115,"props":20689,"children":20690},{},[20691,20696],{"type":22,"tag":2598,"props":20692,"children":20693},{},[20694],{"type":27,"value":20695},"Scalability:",{"type":27,"value":20697}," Can it scale to a large number of components?",{"type":22,"tag":115,"props":20699,"children":20700},{},[20701,20706],{"type":22,"tag":2598,"props":20702,"children":20703},{},[20704],{"type":27,"value":20705},"Simplicity:",{"type":27,"value":20707}," Is it easy to understand and implement?",{"type":22,"tag":115,"props":20709,"children":20710},{},[20711,20716],{"type":22,"tag":2598,"props":20712,"children":20713},{},[20714],{"type":27,"value":20715},"Modifiability:",{"type":22,"tag":111,"props":20717,"children":20718},{},[20719,20729,20739,20749,20759],{"type":22,"tag":115,"props":20720,"children":20721},{},[20722,20727],{"type":22,"tag":2598,"props":20723,"children":20724},{},[20725],{"type":27,"value":20726},"Evolvability:",{"type":27,"value":20728}," Can a component be changed without affecting other components?",{"type":22,"tag":115,"props":20730,"children":20731},{},[20732,20737],{"type":22,"tag":2598,"props":20733,"children":20734},{},[20735],{"type":27,"value":20736},"Extensibility:",{"type":27,"value":20738}," Is it easy to add functionality to the system?",{"type":22,"tag":115,"props":20740,"children":20741},{},[20742,20747],{"type":22,"tag":2598,"props":20743,"children":20744},{},[20745],{"type":27,"value":20746},"Customizability:",{"type":27,"value":20748}," Is it easy to override or specialize its behavior for unusual requirements?",{"type":22,"tag":115,"props":20750,"children":20751},{},[20752,20757],{"type":22,"tag":2598,"props":20753,"children":20754},{},[20755],{"type":27,"value":20756},"Configurability:",{"type":27,"value":20758}," Can it be easily adapted post-deployment?",{"type":22,"tag":115,"props":20760,"children":20761},{},[20762,20767],{"type":22,"tag":2598,"props":20763,"children":20764},{},[20765],{"type":27,"value":20766},"Reusability:",{"type":27,"value":20768}," Are its components generic enough to be reusable for other applications?",{"type":22,"tag":115,"props":20770,"children":20771},{},[20772,20777],{"type":22,"tag":2598,"props":20773,"children":20774},{},[20775],{"type":27,"value":20776},"Visibility:",{"type":27,"value":20778}," Is it easy to view the operation of the system externally?",{"type":22,"tag":115,"props":20780,"children":20781},{},[20782,20787],{"type":22,"tag":2598,"props":20783,"children":20784},{},[20785],{"type":27,"value":20786},"Portability:",{"type":27,"value":20788}," Is it portable across many runtime environments?",{"type":22,"tag":115,"props":20790,"children":20791},{},[20792,20797],{"type":22,"tag":2598,"props":20793,"children":20794},{},[20795],{"type":27,"value":20796},"Reliability:",{"type":27,"value":20798}," How susceptible is it to failure?",{"type":22,"tag":23,"props":20800,"children":20801},{},[20802],{"type":27,"value":20803},"I think this list is such a fantastic set of evaluation criteria that it's worth printing out and using as a checklist for any architectural task. The subtle distinctions between evolvability, extensibility, customizability, configurability and reusability are particularly handy; it's entirely possible to make a super-extensible system that isn't customizable, for example, or a super-customizable system that isn't configurable.",{"type":22,"tag":1481,"props":20805,"children":20807},{"id":20806},"aside-security",[20808],{"type":27,"value":20809},"Aside: Security",{"type":22,"tag":23,"props":20811,"children":20812},{},[20813],{"type":27,"value":20814},"Reading this paper in 2019, one important property seems conspicuously absent from the list:",{"type":22,"tag":111,"props":20816,"children":20817},{},[20818],{"type":22,"tag":115,"props":20819,"children":20820},{},[20821,20826],{"type":22,"tag":2598,"props":20822,"children":20823},{},[20824],{"type":27,"value":20825},"Security:",{"type":27,"value":20827}," Will I get pwned by running this system?",{"type":22,"tag":23,"props":20829,"children":20830},{},[20831,20833,20838],{"type":27,"value":20832},"It's a window into a simpler time, to see security absent from a meticulous list of criteria used to evaluate systems which among other things ",{"type":22,"tag":267,"props":20834,"children":20835},{},[20836],{"type":27,"value":20837},"send raw code back and forth between clients and servers",{"type":27,"value":20839},". In this paper, security sees only casual mentions, and the ability for network firewalls to inspect unencrypted data over-the-wire is seen as a desirable feature.",{"type":22,"tag":23,"props":20841,"children":20842},{},[20843],{"type":27,"value":20844},"While security should be a top consideration for any architecture today, REST stands the test of time by virtue of relying on the security of the underlying protocol on which it's based (HTTP). It's worth noting that it's through other architectural properties that it has been able to achieve this: its evolvability allows HTTPS to be used, and its extensibility allows authentication/authorization to be layered on top of it by applications.",{"type":22,"tag":193,"props":20846,"children":20848},{"id":20847},"chapter-3-network-based-architectural-styles",[20849],{"type":27,"value":20850},"Chapter 3: Network-based Architectural Styles",{"type":22,"tag":23,"props":20852,"children":20853},{},[20854],{"type":27,"value":20855},"With the evaluation properties defined, Fielding evaluates a range of architectural styles, unsurprisingly settling on a REST-like architecture as the winner. This is a hard chapter to summarize, so I'd recommend reading through it in full. Essentially, each style is given an acronym so that combinations of styles can be referred to by one big acronym.",{"type":22,"tag":23,"props":20857,"children":20858},{},[20859],{"type":27,"value":20860},"Styles evaluated are (with very brief descriptions provided here when relevant):",{"type":22,"tag":111,"props":20862,"children":20863},{},[20864,20882,20900,20948,20981],{"type":22,"tag":115,"props":20865,"children":20866},{},[20867,20869],{"type":27,"value":20868},"Data-flow Styles\n",{"type":22,"tag":111,"props":20870,"children":20871},{},[20872,20877],{"type":22,"tag":115,"props":20873,"children":20874},{},[20875],{"type":27,"value":20876},"Pipe and Filter (PF)",{"type":22,"tag":115,"props":20878,"children":20879},{},[20880],{"type":27,"value":20881},"Uniform Pipe and Filter (UPF): For example, Unix command-line applications",{"type":22,"tag":115,"props":20883,"children":20884},{},[20885,20887],{"type":27,"value":20886},"Replication Styles\n",{"type":22,"tag":111,"props":20888,"children":20889},{},[20890,20895],{"type":22,"tag":115,"props":20891,"children":20892},{},[20893],{"type":27,"value":20894},"Replicated Repository (RR): Replicating data across multiple services",{"type":22,"tag":115,"props":20896,"children":20897},{},[20898],{"type":27,"value":20899},"Cache ($): RR variant where only requested data is replicated",{"type":22,"tag":115,"props":20901,"children":20902},{},[20903,20905],{"type":27,"value":20904},"Hierarchical Styles\n",{"type":22,"tag":111,"props":20906,"children":20907},{},[20908,20913,20918,20923,20928,20933,20938,20943],{"type":22,"tag":115,"props":20909,"children":20910},{},[20911],{"type":27,"value":20912},"Client-Server (CS)",{"type":22,"tag":115,"props":20914,"children":20915},{},[20916],{"type":27,"value":20917},"Layered System (LS): System organized into a hierarchy of layers",{"type":22,"tag":115,"props":20919,"children":20920},{},[20921],{"type":27,"value":20922},"Layered-Client-Server (LCS)",{"type":22,"tag":115,"props":20924,"children":20925},{},[20926],{"type":27,"value":20927},"Client-Stateless-Server (CSS)",{"type":22,"tag":115,"props":20929,"children":20930},{},[20931],{"type":27,"value":20932},"Client-Cache-Stateless-Server (C$SS)",{"type":22,"tag":115,"props":20934,"children":20935},{},[20936],{"type":27,"value":20937},"Layered-Client-Cache-Stateless-Server (LC$SS)",{"type":22,"tag":115,"props":20939,"children":20940},{},[20941],{"type":27,"value":20942},"Remote Session (RS): Application state kept entirely on the server",{"type":22,"tag":115,"props":20944,"children":20945},{},[20946],{"type":27,"value":20947},"Remote Data Access (RDA): Client performs database queries directly on the server",{"type":22,"tag":115,"props":20949,"children":20950},{},[20951,20953],{"type":27,"value":20952},"Mobile Code Styles:\n",{"type":22,"tag":111,"props":20954,"children":20955},{},[20956,20961,20966,20971,20976],{"type":22,"tag":115,"props":20957,"children":20958},{},[20959],{"type":27,"value":20960},"Virtual Machine (VM): Executing code in a controlled environment",{"type":22,"tag":115,"props":20962,"children":20963},{},[20964],{"type":27,"value":20965},"Remote Evaluation (REV): Delegating expensive operations to a remote site for evaluation",{"type":22,"tag":115,"props":20967,"children":20968},{},[20969],{"type":27,"value":20970},"Code on Demand (COD): Component queries a remote server for the know-how to process a resource",{"type":22,"tag":115,"props":20972,"children":20973},{},[20974],{"type":27,"value":20975},"Layered-Code-on-Demand-Client-Cache-Stateless-Server (LCODC$SS)",{"type":22,"tag":115,"props":20977,"children":20978},{},[20979],{"type":27,"value":20980},"Mobile Agent (MA): Extension of RE and COD style where the agent performing computation \"moves\" from client to\nserver and back",{"type":22,"tag":115,"props":20982,"children":20983},{},[20984,20986],{"type":27,"value":20985},"Peer-to-Peer Styles:\n",{"type":22,"tag":111,"props":20987,"children":20988},{},[20989,20994,20999,21004],{"type":22,"tag":115,"props":20990,"children":20991},{},[20992],{"type":27,"value":20993},"Event-based Integration (EBI): components trigger and listen for events rather than integrating directly with other\ncomponents",{"type":22,"tag":115,"props":20995,"children":20996},{},[20997],{"type":27,"value":20998},"C2",{"type":22,"tag":115,"props":21000,"children":21001},{},[21002],{"type":27,"value":21003},"Distributed Objects",{"type":22,"tag":115,"props":21005,"children":21006},{},[21007],{"type":27,"value":21008},"Brokered Distributed Objects",{"type":22,"tag":23,"props":21010,"children":21011},{},[21012,21014,21019,21020,21025],{"type":27,"value":21013},"Here's the final evaluation summary from the chapter: each property has ",{"type":22,"tag":486,"props":21015,"children":21017},{"className":21016},[],[21018],{"type":27,"value":3713},{"type":27,"value":2684},{"type":22,"tag":486,"props":21021,"children":21023},{"className":21022},[],[21024],{"type":27,"value":4207},{"type":27,"value":21026}," characters proportional to a rough measure of how beneficial or detrimental the style is to that property.",{"type":22,"tag":23,"props":21028,"children":21029},{},[21030],{"type":22,"tag":314,"props":21031,"children":21034},{"alt":21032,"src":21033},"The evaluation summary, in its full terse glory","/phendry/2019-3/img/chap3_comparison.png",[],{"type":22,"tag":23,"props":21036,"children":21037},{},[21038,21040,21045,21046,21051,21052,21057,21058,21063],{"type":27,"value":21039},"In the end, we find that \"LCODC$SS\" appears to be the overall winner; that's short for ",{"type":22,"tag":267,"props":21041,"children":21042},{},[21043],{"type":27,"value":21044},"Layered",{"type":27,"value":236},{"type":22,"tag":267,"props":21047,"children":21048},{},[21049],{"type":27,"value":21050},"Code-On-Demand",{"type":27,"value":236},{"type":22,"tag":267,"props":21053,"children":21054},{},[21055],{"type":27,"value":21056},"Client Cache",{"type":27,"value":236},{"type":22,"tag":267,"props":21059,"children":21060},{},[21061],{"type":27,"value":21062},"Stateless Server",{"type":27,"value":21064},". Sound familiar? With \"layering\" referring to gateways and proxies, \"code on demand\" referring to JavaScript, and caching/statelessness being common aspects of HTTP services, what we're describing here is the Web as we know it.",{"type":22,"tag":23,"props":21066,"children":21067},{},[21068],{"type":27,"value":21069},"This analysis not only provides the justification for the fundamental properties of REST as an architecture, but like Chapter 2, it's a useful reminder of everything that's out there for you to consider. Many of the architectures considered here have fallen out of favour, but that doesn't mean they're not still relevant today; every problem is different, and while REST works well for traditional web resources, it's not the right tool for every job.",{"type":22,"tag":1481,"props":21071,"children":21073},{"id":21072},"aside-remote-data-access",[21074],{"type":27,"value":21075},"Aside: \"Remote Data Access\"",{"type":22,"tag":23,"props":21077,"children":21078},{},[21079],{"type":27,"value":21080},"As a side note, I couldn't help but notice that one of the mentioned styles sounded familiar...",{"type":22,"tag":21082,"props":21083,"children":21084},"blockquote",{},[21085],{"type":22,"tag":23,"props":21086,"children":21087},{},[21088,21089,21094],{"type":27,"value":10237},{"type":22,"tag":2598,"props":21090,"children":21091},{},[21092],{"type":27,"value":21093},"remote data access",{"type":27,"value":21095}," style is a variant of client-server that spreads the application state across both\nclient and server. A client sends a database query in a standard format, such as SQL, to a remote server. The server\nallocates a workspace and performs the query, which may result in a very large data set. The client can then make\nfurther operations upon the result set (such as table joins) or retrieve the result one piece at a time. The client\nmust know about the data structure of the service to build structure-dependent queries.",{"type":22,"tag":23,"props":21097,"children":21098},{},[21099,21101,21108],{"type":27,"value":21100},"What's this? How did ",{"type":22,"tag":30,"props":21102,"children":21105},{"href":21103,"rel":21104},"https://graphql.org/learn/",[34],[21106],{"type":27,"value":21107},"GraphQL",{"type":27,"value":21109}," get into this paper from the year 2000?",{"type":22,"tag":23,"props":21111,"children":21112},{},[21113],{"type":27,"value":21114},"It's good to be reminded that some of these hot new tools are iterations on ideas that are decades old.",{"type":22,"tag":193,"props":21116,"children":21118},{"id":21117},"chapter-4-designing-the-web-architecture-problems-and-insights",[21119],{"type":27,"value":21120},"Chapter 4: Designing the Web Architecture: Problems and Insights",{"type":22,"tag":23,"props":21122,"children":21123},{},[21124],{"type":27,"value":21125},"To expand on the general properties from Chapter 2, Fielding describes some further requirements that are specific to Web architectures:",{"type":22,"tag":111,"props":21127,"children":21128},{},[21129,21152,21157,21162],{"type":22,"tag":115,"props":21130,"children":21131},{},[21132,21134],{"type":27,"value":21133},"A low barrier to entry for everyone (readers, authors, and developers). Hypertext and HTML were chosen for this because:\n",{"type":22,"tag":111,"props":21135,"children":21136},{},[21137,21142,21147],{"type":22,"tag":115,"props":21138,"children":21139},{},[21140],{"type":27,"value":21141},"For users, it's simple to navigate pages via hyperlinks;",{"type":22,"tag":115,"props":21143,"children":21144},{},[21145],{"type":27,"value":21146},"For authors, it's simple to link to content, in particular because the content itself doesn't need to exist yet in\norder to define a link;",{"type":22,"tag":115,"props":21148,"children":21149},{},[21150],{"type":27,"value":21151},"For developers, it's simple to implement because all the protocols are defined as text, making them easy to inspect\nand interactively test.",{"type":22,"tag":115,"props":21153,"children":21154},{},[21155],{"type":27,"value":21156},"Extensibility",{"type":22,"tag":115,"props":21158,"children":21159},{},[21160],{"type":27,"value":21161},"Distributed content",{"type":22,"tag":115,"props":21163,"children":21164},{},[21165],{"type":27,"value":21166},"Internet-scale (in the sense that it needs to be tolerant of a chaotic Internet where network traffic is unpredictable and servers can be deployed/retired at will)",{"type":22,"tag":23,"props":21168,"children":21169},{},[21170],{"type":27,"value":21171},"He also describes some of the additional challenges of trying to achieve these goals while building on top of the existing URI, HTTP and HTML standards rather than replacing them.",{"type":22,"tag":193,"props":21173,"children":21175},{"id":21174},"chapter-5-representational-state-transfer-rest",[21176],{"type":27,"value":21177},"Chapter 5: Representational State Transfer (REST)",{"type":22,"tag":23,"props":21179,"children":21180},{},[21181],{"type":27,"value":21182},"With all of that background out of the way, we finally turn to deriving REST itself. In proper scientific fashion, we start with a \"null\" architecture and build upon it using the constraints we've been laying out in previous chapters.",{"type":22,"tag":1481,"props":21184,"children":21186},{"id":21185},"deriving-rest",[21187],{"type":27,"value":21188},"Deriving REST",{"type":22,"tag":23,"props":21190,"children":21191},{},[21192],{"type":27,"value":21193},"The first two constraints are the client-server and stateless styles. Both of these are deemed necessary just due to the scale of the Internet: we keep implementations simple by separating the providers of the content (servers) from the consumers (clients), and we can't afford to have servers maintain per-client state if they're to work at the scale we envision.",{"type":22,"tag":23,"props":21195,"children":21196},{},[21197],{"type":27,"value":21198},"The next constraint is caching: if we're going to minimize network usage and user-perceived latency, we want an architecture that allows us to cache content effectively.",{"type":22,"tag":23,"props":21200,"children":21201},{},[21202],{"type":27,"value":21203},"Before we go any further, let's just admire the diagram displaying \"Internet News\" with a big exciting lightning bolt:",{"type":22,"tag":23,"props":21205,"children":21206},{},[21207],{"type":22,"tag":314,"props":21208,"children":21211},{"alt":21209,"src":21210},"Snippet of a diagram in the paper with object titled \"Internet News\" accompanied by a lightning bolt","/phendry/2019-3/img/chap5_internet_news.png",[],{"type":22,"tag":23,"props":21213,"children":21214},{},[21215],{"type":27,"value":21216},"Back to constraints, we add \"uniform interface\" next. Fielding describes this as the core thing that differentiates REST from other architectural styles. REST ensures that every service works through a uniform interface, which aims to improve the simplicity and scalability of the architecture (at a modest cost to network efficiency). The constraints which achieve this uniform interface are described later.",{"type":22,"tag":23,"props":21218,"children":21219},{},[21220],{"type":27,"value":21221},"Nearly finished with the constraining now! The penultimate constraint is layering: the ability to build hierarchical layers in the system to encapsulate or \"hide\" different layers. This enables a great deal of scalability, e.g. with load balancers spreading traffic across a number of servers, and it also provides the flexibility to evolve systems by\nintroducing layers to handle legacy servers or clients. The downside of layering is the extra latency/overhead, but we're hoping that we can offset this with clever use of caching.",{"type":22,"tag":23,"props":21223,"children":21224},{},[21225],{"type":27,"value":21226},"Finally, we've got code-on-demand: enabling (or at least not preventing) servers to supply code to clients for the client to execute. In the age of endless web apps this seems obvious, but I think it's worth appreciating that this got factored in to Web specifications as early as it did. JavaScript was only a few years old at the time, and it wasn't at all clear at that point that scripting would be an integral part of the Web.",{"type":22,"tag":1481,"props":21228,"children":21230},{"id":21229},"rest-architectural-elements",[21231],{"type":27,"value":21232},"REST Architectural Elements",{"type":22,"tag":23,"props":21234,"children":21235},{},[21236],{"type":27,"value":21237},"Chapter 5.2 is where we finally define REST in concrete terms.",{"type":22,"tag":23,"props":21239,"children":21240},{},[21241],{"type":27,"value":21242},"Many people jump straight to this section when trying to better understand REST, but this tells you the \"what\" without the \"why\" from the earlier chapters. Without understanding the derivation of REST, it's easy to misunderstand the problems that REST aims to solve.",{"type":22,"tag":23,"props":21244,"children":21245},{},[21246],{"type":27,"value":21247},"Here's an overview of its architectural elements:",{"type":22,"tag":21249,"props":21250,"children":21252},"h4",{"id":21251},"data-elements",[21253],{"type":27,"value":21254},"Data Elements",{"type":22,"tag":23,"props":21256,"children":21257},{},[21258,21259,21264,21266,21271,21273,21277,21279,21283],{"type":27,"value":16869},{"type":22,"tag":267,"props":21260,"children":21261},{},[21262],{"type":27,"value":21263},"resource",{"type":27,"value":21265}," is essentially any information that can be named, and a ",{"type":22,"tag":267,"props":21267,"children":21268},{},[21269],{"type":27,"value":21270},"resource identifier",{"type":27,"value":21272}," is a means to access a ",{"type":22,"tag":267,"props":21274,"children":21275},{},[21276],{"type":27,"value":21263},{"type":27,"value":21278}," (usually a URL). A key property of resources is that they do ",{"type":22,"tag":267,"props":21280,"children":21281},{},[21282],{"type":27,"value":869},{"type":27,"value":21284}," necessarily have any direct correspondence with an underlying representation or storage location. For example:",{"type":22,"tag":111,"props":21286,"children":21287},{},[21288,21308,21313],{"type":22,"tag":115,"props":21289,"children":21290},{},[21291,21293,21299,21300,21306],{"type":27,"value":21292},"Two companies 1 and 2 might have similar identifiers (",{"type":22,"tag":486,"props":21294,"children":21296},{"className":21295},[],[21297],{"type":27,"value":21298},"/companies/1",{"type":27,"value":1441},{"type":22,"tag":486,"props":21301,"children":21303},{"className":21302},[],[21304],{"type":27,"value":21305},"/companies/2",{"type":27,"value":21307},") even though their data resides on separate servers;",{"type":22,"tag":115,"props":21309,"children":21310},{},[21311],{"type":27,"value":21312},"\"Companies that user ABC has favorited\" is a resource, even though it describes information which can change over time;",{"type":22,"tag":115,"props":21314,"children":21315},{},[21316],{"type":27,"value":21317},"\"Currently active users\" is a resource even though it's constructed from live information that isn't persisted anywhere.",{"type":22,"tag":23,"props":21319,"children":21320},{},[21321],{"type":27,"value":21322},"While this is a simple concept, libraries like Ruby on Rails and Django Rest Framework make directly converting backend models into REST resources so easy that developers often fall into it as the default. This can create an undesirable coupling between client and server (where every backend database change requires a corresponding frontend change), and unnecessary frontend complexity (where clients collect and process raw data rather than rely on the server to present it to them that way).",{"type":22,"tag":21249,"props":21324,"children":21326},{"id":21325},"connectors-and-components",[21327],{"type":27,"value":21328},"Connectors and Components",{"type":22,"tag":23,"props":21330,"children":21331},{},[21332],{"type":27,"value":21333},"REST separates concerns with a variety of \"connector\" types: Client, Server, Cache, Resolver (i.e. DNS), and Tunnel (e.g. TLS).",{"type":22,"tag":23,"props":21335,"children":21336},{},[21337],{"type":27,"value":21338},"A key aspect is the stateless communication between these connectors. Statelessness accomplishes four things:",{"type":22,"tag":847,"props":21340,"children":21341},{},[21342,21347,21352,21357],{"type":22,"tag":115,"props":21343,"children":21344},{},[21345],{"type":27,"value":21346},"It reduces the resource requirements of the connectors",{"type":22,"tag":115,"props":21348,"children":21349},{},[21350],{"type":27,"value":21351},"It allows parallel processing of requests",{"type":22,"tag":115,"props":21353,"children":21354},{},[21355],{"type":27,"value":21356},"It allows an intermediary to understand a request in isolation, which is useful for dynamically modifying/reordering\nrequests",{"type":22,"tag":115,"props":21358,"children":21359},{},[21360],{"type":27,"value":21361},"It forces all the information that's relevant for caching to be present in the request",{"type":22,"tag":21249,"props":21363,"children":21365},{"id":21364},"overall-view",[21366],{"type":27,"value":21367},"Overall View",{"type":22,"tag":23,"props":21369,"children":21370},{},[21371],{"type":27,"value":21372},"With all these components in place, we have:",{"type":22,"tag":111,"props":21374,"children":21375},{},[21376,21381,21386,21391,21396],{"type":22,"tag":115,"props":21377,"children":21378},{},[21379],{"type":27,"value":21380},"User Agent which can submit concurrent stateless requests",{"type":22,"tag":115,"props":21382,"children":21383},{},[21384],{"type":27,"value":21385},"A DNS service which translates request URLs into addressable network locations",{"type":22,"tag":115,"props":21387,"children":21388},{},[21389],{"type":27,"value":21390},"The ability to have Proxies, allowing clients to access resources through an intermediate location",{"type":22,"tag":115,"props":21392,"children":21393},{},[21394],{"type":27,"value":21395},"The ability to have Gateways (i.e. reverse proxies), allowing servers to provided resources through an intermediate\nlocation",{"type":22,"tag":115,"props":21397,"children":21398},{},[21399],{"type":27,"value":21400},"The capability of introducing caching at any of these layers in order to avoid redundant network requests",{"type":22,"tag":23,"props":21402,"children":21403},{},[21404],{"type":27,"value":21405},"As an example, we can model a Process View of a complex system, showing off what's possible with the flexibility of this architecture:",{"type":22,"tag":23,"props":21407,"children":21408},{},[21409],{"type":22,"tag":314,"props":21410,"children":21413},{"alt":21411,"src":21412},"Process View of a REST system","/phendry/2019-3/img/chap5_rest_view.png",[],{"type":22,"tag":193,"props":21415,"children":21417},{"id":21416},"chapter-6-experience-and-evaluation",[21418],{"type":27,"value":21419},"Chapter 6: Experience and Evaluation",{"type":22,"tag":23,"props":21421,"children":21422},{},[21423],{"type":27,"value":21424},"While interesting due to the historical context it provides, we'll skip this section for today since most of the details here aren't as relevant to modern-day systems.",{"type":22,"tag":193,"props":21426,"children":21427},{"id":11945},[21428],{"type":27,"value":11948},{"type":22,"tag":23,"props":21430,"children":21431},{},[21432],{"type":27,"value":21433},"Going into this, I was hoping to gain specific insights on building good RESTful APIs. What I got instead was insight into software architecture design in the very broadest sense. The bigger take-away from this dissertation, for someone like me at least, is not any particular detail about REST itself but rather the overall design process that created it. The evaluation criteria from Chapter 2 (performance, reusability, etc) and the architectural styles from Chapter 3 are as useful for defining your own architecture as they are for defining REST.",{"type":22,"tag":23,"props":21435,"children":21436},{},[21437,21439,21444,21446,21451],{"type":27,"value":21438},"And while brushing up on these foundational REST concepts doesn't directly answer any of those practical \"detail questions\" when building Web APIs, it's... more immediately helpful than you might think. If you can't recall why modifying state in response to a GET request is OK, you can \"derive\" the answer by remembering that a caching layer could stop repeated GET requests from hitting the server. If you're not sure how to version your API, you can remember that both URIs and headers affect caching, so either way will do. If you're not sure whether to choose ",{"type":22,"tag":486,"props":21440,"children":21442},{"className":21441},[],[21443],{"type":27,"value":20511},{"type":27,"value":21445}," as a resource vs ",{"type":22,"tag":486,"props":21447,"children":21449},{"className":21448},[],[21450],{"type":27,"value":20519},{"type":27,"value":21452},", you can remember that resources don't need to\nhave any relation to the underlying data, so you can choose whichever one is nicer to use in the client.",{"type":22,"tag":23,"props":21454,"children":21455},{},[21456],{"type":27,"value":21457},"Perhaps most of all, it's a good reminder that most of time when we developers debate what's \"RESTful\" and what's \"not RESTful\", what we're really debating is conventions. The flexibility of REST is the reason we have so much to debate, but it's also the reason that REST is everywhere.",{"title":8,"searchDepth":755,"depth":755,"links":21459},[21460,21461,21464,21467,21468,21476,21477],{"id":20547,"depth":758,"text":20550},{"id":20630,"depth":758,"text":20633,"children":21462},[21463],{"id":20806,"depth":755,"text":20809},{"id":20847,"depth":758,"text":20850,"children":21465},[21466],{"id":21072,"depth":755,"text":21075},{"id":21117,"depth":758,"text":21120},{"id":21174,"depth":758,"text":21177,"children":21469},[21470,21471],{"id":21185,"depth":755,"text":21188},{"id":21229,"depth":755,"text":21232,"children":21472},[21473,21474,21475],{"id":21251,"depth":983,"text":21254},{"id":21325,"depth":983,"text":21328},{"id":21364,"depth":983,"text":21367},{"id":21416,"depth":758,"text":21419},{"id":11945,"depth":758,"text":11948},"content:phendry:2019-3:RestFromTheBottomUp.md","phendry/2019-3/RestFromTheBottomUp.md","phendry/2019-3/RestFromTheBottomUp",{"user":774,"name":775},1780330263382]